diff --git a/doc/api/util.md b/doc/api/util.md
index a001124824f0a5..bc1d3ae262ae50 100644
--- a/doc/api/util.md
+++ b/doc/api/util.md
@@ -316,6 +316,9 @@ stream.write('With ES6');
 <!-- YAML
 added: v0.3.0
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/REPLACEME
+    description: The `compact` option is supported now.
   - version: v6.6.0
     pr-url: https://github.com/nodejs/node/pull/8174
     description: Custom inspection functions can now return `this`.
@@ -353,6 +356,13 @@ changes:
   * `breakLength` {number} The length at which an object's keys are split
     across multiple lines. Set to `Infinity` to format an object as a single
     line. Defaults to 60 for legacy compatibility.
+  * `compact` {boolean} Setting this to `false` changes the default indentation
+    to use a line break for each object key instead of lining up multiple
+    properties in one line. It will also break text that is above the
+    `breakLength` size into smaller and better readable chunks and indents
+    objects the same as arrays. Note that no text will be reduced below 16
+    characters, no matter the `breakLength` size. For more information, see the
+    example below. Defaults to `true`.
 
 The `util.inspect()` method returns a string representation of `object` that is
 primarily useful for debugging. Additional `options` may be passed that alter
@@ -370,6 +380,63 @@ Values may supply their own custom `inspect(depth, opts)` functions, when
 called these receive the current `depth` in the recursive inspection, as well as
 the options object passed to `util.inspect()`.
 
+The following example highlights the difference with the `compact` option:
+
+```js
+const util = require('util');
+
+const o = {
+  a: [1, 2, [[
+    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ' +
+      'eiusmod tempor incididunt ut labore et dolore magna aliqua.',
+    'test',
+    'foo']], 4],
+  b: new Map([['za', 1], ['zb', 'test']])
+};
+console.log(util.inspect(o, { compact: true, depth: 5, breakLength: 80 }));
+
+// This will print
+
+// { a:
+//   [ 1,
+//     2,
+//     [ [ 'Lorem ipsum dolor sit amet, consectetur [...]', // A long line
+//           'test',
+//           'foo' ] ],
+//     4 ],
+//   b: Map { 'za' => 1, 'zb' => 'test' } }
+
+// Setting `compact` to false changes the output to be more reader friendly.
+console.log(util.inspect(o, { compact: false, depth: 5, breakLength: 80 }));
+
+// {
+//   a: [
+//     1,
+//     2,
+//     [
+//       [
+//         'Lorem ipsum dolor sit amet, consectetur ' +
+//           'adipiscing elit, sed do eiusmod tempor ' +
+//           'incididunt ut labore et dolore magna ' +
+//           'aliqua.,
+//         'test',
+//         'foo'
+//       ]
+//     ],
+//     4
+//   ],
+//   b: Map {
+//     'za' => 1,
+//     'zb' => 'test'
+//   }
+// }
+
+// Setting `breakLength` to e.g. 150 will print the "Lorem ipsum" text in a
+// single line.
+// Reducing the `breakLength` will split the "Lorem ipsum" text in smaller
+// chunks.
+```
+
 ### Customizing `util.inspect` colors
 
 <!-- type=misc -->
diff --git a/lib/util.js b/lib/util.js
index bb805a082b27d6..70fd1a05564389 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -68,7 +68,8 @@ const inspectDefaultOptions = Object.seal({
   customInspect: true,
   showProxy: false,
   maxArrayLength: 100,
-  breakLength: 60
+  breakLength: 60,
+  compact: true
 });
 
 const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
@@ -86,6 +87,10 @@ const strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c]/g;
 const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
 const numberRegExp = /^(0|[1-9][0-9]*)$/;
 
+const readableRegExps = {};
+
+const MIN_LINE_LENGTH = 16;
+
 // Escaped special characters. Use empty strings to fill up unused entries.
 const meta = [
   '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
@@ -276,7 +281,8 @@ function inspect(value, opts) {
     showProxy: inspectDefaultOptions.showProxy,
     maxArrayLength: inspectDefaultOptions.maxArrayLength,
     breakLength: inspectDefaultOptions.breakLength,
-    indentationLvl: 0
+    indentationLvl: 0,
+    compact: inspectDefaultOptions.compact
   };
   // Legacy...
   if (arguments.length > 2) {
@@ -374,7 +380,7 @@ function ensureDebugIsInitialized() {
 function formatValue(ctx, value, recurseTimes, ln) {
   // Primitive types cannot have properties
   if (typeof value !== 'object' && typeof value !== 'function') {
-    return formatPrimitive(ctx.stylize, value);
+    return formatPrimitive(ctx.stylize, value, ctx);
   }
   if (value === null) {
     return ctx.stylize('null', 'null');
@@ -481,10 +487,10 @@ function formatValue(ctx, value, recurseTimes, ln) {
       } catch (e) { /* ignore */ }
 
       if (typeof raw === 'string') {
-        const formatted = formatPrimitive(stylizeNoColor, raw);
+        const formatted = formatPrimitive(stylizeNoColor, raw, ctx);
         if (keyLength === raw.length)
           return ctx.stylize(`[String: ${formatted}]`, 'string');
-        base = ` [String: ${formatted}]`;
+        base = `[String: ${formatted}]`;
         // For boxed Strings, we have to remove the 0-n indexed entries,
         // since they just noisy up the output and are redundant
         // Make boxed primitive Strings look like such
@@ -505,12 +511,12 @@ function formatValue(ctx, value, recurseTimes, ln) {
       const name = `${constructor.name}${value.name ? `: ${value.name}` : ''}`;
       if (keyLength === 0)
         return ctx.stylize(`[${name}]`, 'special');
-      base = ` [${name}]`;
+      base = `[${name}]`;
     } else if (isRegExp(value)) {
       // Make RegExps say that they are RegExps
       if (keyLength === 0 || recurseTimes < 0)
         return ctx.stylize(regExpToString.call(value), 'regexp');
-      base = ` ${regExpToString.call(value)}`;
+      base = `${regExpToString.call(value)}`;
     } else if (isDate(value)) {
       if (keyLength === 0) {
         if (Number.isNaN(value.getTime()))
@@ -518,12 +524,12 @@ function formatValue(ctx, value, recurseTimes, ln) {
         return ctx.stylize(dateToISOString.call(value), 'date');
       }
       // Make dates with properties first say the date
-      base = ` ${dateToISOString.call(value)}`;
+      base = `${dateToISOString.call(value)}`;
     } else if (isError(value)) {
       // Make error with message first say the error
       if (keyLength === 0)
         return formatError(value);
-      base = ` ${formatError(value)}`;
+      base = `${formatError(value)}`;
     } else if (isAnyArrayBuffer(value)) {
       // Fast path for ArrayBuffer and SharedArrayBuffer.
       // Can't do the same for DataView because it has a non-primitive
@@ -553,13 +559,13 @@ function formatValue(ctx, value, recurseTimes, ln) {
         const formatted = formatPrimitive(stylizeNoColor, raw);
         if (keyLength === 0)
           return ctx.stylize(`[Number: ${formatted}]`, 'number');
-        base = ` [Number: ${formatted}]`;
+        base = `[Number: ${formatted}]`;
       } else if (typeof raw === 'boolean') {
         // Make boxed primitive Booleans look like such
         const formatted = formatPrimitive(stylizeNoColor, raw);
         if (keyLength === 0)
           return ctx.stylize(`[Boolean: ${formatted}]`, 'boolean');
-        base = ` [Boolean: ${formatted}]`;
+        base = `[Boolean: ${formatted}]`;
       } else if (typeof raw === 'symbol') {
         const formatted = formatPrimitive(stylizeNoColor, raw);
         return ctx.stylize(`[Symbol: ${formatted}]`, 'symbol');
@@ -603,9 +609,42 @@ function formatNumber(fn, value) {
   return fn(`${value}`, 'number');
 }
 
-function formatPrimitive(fn, value) {
-  if (typeof value === 'string')
+function formatPrimitive(fn, value, ctx) {
+  if (typeof value === 'string') {
+    if (ctx.compact === false &&
+      value.length > MIN_LINE_LENGTH &&
+      ctx.indentationLvl + value.length > ctx.breakLength) {
+      // eslint-disable-next-line max-len
+      const minLineLength = Math.max(ctx.breakLength - ctx.indentationLvl, MIN_LINE_LENGTH);
+      // eslint-disable-next-line max-len
+      const averageLineLength = Math.ceil(value.length / Math.ceil(value.length / minLineLength));
+      const divisor = Math.max(averageLineLength, MIN_LINE_LENGTH);
+      var res = '';
+      if (readableRegExps[divisor] === undefined) {
+        // Build a new RegExp that naturally breaks text into multiple lines.
+        //
+        // Rules
+        // 1. Greedy match all text up the max line length that ends with a
+        //    whitespace or the end of the string.
+        // 2. If none matches, non-greedy match any text up to a whitespace or
+        //    the end of the string.
+        //
+        // eslint-disable-next-line max-len, no-unescaped-regexp-dot
+        readableRegExps[divisor] = new RegExp(`(.|\\n){1,${divisor}}(\\s|$)|(\\n|.)+?(\\s|$)`, 'gm');
+      }
+      const indent = ' '.repeat(ctx.indentationLvl);
+      const matches = value.match(readableRegExps[divisor]);
+      if (matches.length > 1) {
+        res += `${fn(strEscape(matches[0]), 'string')} +\n`;
+        for (var i = 1; i < matches.length - 1; i++) {
+          res += `${indent}  ${fn(strEscape(matches[i]), 'string')} +\n`;
+        }
+        res += `${indent}  ${fn(strEscape(matches[i]), 'string')}`;
+        return res;
+      }
+    }
     return fn(strEscape(value), 'string');
+  }
   if (typeof value === 'number')
     return formatNumber(fn, value);
   if (typeof value === 'boolean')
@@ -806,7 +845,7 @@ function formatProperty(ctx, value, recurseTimes, key, array) {
   const desc = Object.getOwnPropertyDescriptor(value, key) ||
     { value: value[key], enumerable: true };
   if (desc.value !== undefined) {
-    const diff = array === 0 ? 3 : 2;
+    const diff = array !== 0 || ctx.compact === false ? 2 : 3;
     ctx.indentationLvl += diff;
     str = formatValue(ctx, desc.value, recurseTimes, array === 0);
     ctx.indentationLvl -= diff;
@@ -839,9 +878,19 @@ function formatProperty(ctx, value, recurseTimes, key, array) {
 
 function reduceToSingleString(ctx, output, base, braces, addLn) {
   const breakLength = ctx.breakLength;
+  var i = 0;
+  if (ctx.compact === false) {
+    const indentation = ' '.repeat(ctx.indentationLvl);
+    var res = `${base ? `${base} ` : ''}${braces[0]}\n${indentation}  `;
+    for (; i < output.length - 1; i++) {
+      res += `${output[i]},\n${indentation}  `;
+    }
+    res += `${output[i]}\n${indentation}${braces[1]}`;
+    return res;
+  }
   if (output.length * 2 <= breakLength) {
     var length = 0;
-    for (var i = 0; i < output.length && length <= breakLength; i++) {
+    for (; i < output.length && length <= breakLength; i++) {
       if (ctx.colors) {
         length += removeColors(output[i]).length + 1;
       } else {
@@ -849,7 +898,8 @@ function reduceToSingleString(ctx, output, base, braces, addLn) {
       }
     }
     if (length <= breakLength)
-      return `${braces[0]}${base} ${join(output, ', ')} ${braces[1]}`;
+      return `${braces[0]}${base ? ` ${base}` : ''} ${join(output, ', ')} ` +
+        braces[1];
   }
   // If the opening "brace" is too large, like in the case of "Set {",
   // we need to force the first item to be on the next line or the
@@ -857,7 +907,7 @@ function reduceToSingleString(ctx, output, base, braces, addLn) {
   const indentation = ' '.repeat(ctx.indentationLvl);
   const extraLn = addLn === true ? `\n${indentation}` : '';
   const ln = base === '' && braces[0].length === 1 ?
-    ' ' : `${base}\n${indentation}  `;
+    ' ' : `${base ? ` ${base}` : base}\n${indentation}  `;
   const str = join(output, `,\n${indentation}  `);
   return `${extraLn}${braces[0]}${ln}${str} ${braces[1]}`;
 }
diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js
index a99583d454b3f4..83f6b469d68055 100644
--- a/test/parallel/test-util-inspect.js
+++ b/test/parallel/test-util-inspect.js
@@ -1160,3 +1160,146 @@ if (typeof Symbol !== 'undefined') {
 
 assert.doesNotThrow(() => util.inspect(process));
 /* eslint-enable accessor-pairs */
+
+// Setting custom inspect property to a non-function should do nothing.
+{
+  const obj = { inspect: 'fhqwhgads' };
+  assert.strictEqual(util.inspect(obj), "{ inspect: 'fhqwhgads' }");
+}
+
+{
+  const o = {
+    a: [1, 2, [[
+      'Lorem ipsum dolor\nsit amet,\tconsectetur adipiscing elit, sed do ' +
+        'eiusmod tempor incididunt ut labore et dolore magna aliqua.',
+      'test',
+      'foo']], 4],
+    b: new Map([['za', 1], ['zb', 'test']])
+  };
+
+  let out = util.inspect(o, { compact: true, depth: 5, breakLength: 80 });
+  let expect = [
+    '{ a: ',
+    '   [ 1,',
+    '     2,',
+    "     [ [ 'Lorem ipsum dolor\\nsit amet,\\tconsectetur adipiscing elit, " +
+      "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',",
+    "         'test',",
+    "         'foo' ] ],",
+    '     4 ],',
+    "  b: Map { 'za' => 1, 'zb' => 'test' } }",
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(o, { compact: false, depth: 5, breakLength: 60 });
+  expect = [
+    '{',
+    '  a: [',
+    '    1,',
+    '    2,',
+    '    [',
+    '      [',
+    '        \'Lorem ipsum dolor\\nsit amet,\\tconsectetur \' +',
+    '          \'adipiscing elit, sed do eiusmod tempor \' +',
+    '          \'incididunt ut labore et dolore magna \' +',
+    '          \'aliqua.\',',
+    '        \'test\',',
+    '        \'foo\'',
+    '      ]',
+    '    ],',
+    '    4',
+    '  ],',
+    '  b: Map {',
+    '    \'za\' => 1,',
+    '    \'zb\' => \'test\'',
+    '  }',
+    '}'
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(o.a[2][0][0], { compact: false, breakLength: 30 });
+  expect = [
+    '\'Lorem ipsum dolor\\nsit \' +',
+    '  \'amet,\\tconsectetur \' +',
+    '  \'adipiscing elit, sed do \' +',
+    '  \'eiusmod tempor incididunt \' +',
+    '  \'ut labore et dolore magna \' +',
+    '  \'aliqua.\''
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(
+    '12345678901234567890123456789012345678901234567890',
+    { compact: false, breakLength: 3 });
+  expect = '\'12345678901234567890123456789012345678901234567890\'';
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(
+    '12 45 78 01 34 67 90 23 56 89 123456789012345678901234567890',
+    { compact: false, breakLength: 3 });
+  expect = [
+    '\'12 45 78 01 34 \' +',
+    '  \'67 90 23 56 89 \' +',
+    '  \'123456789012345678901234567890\''
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(
+    '12 45 78 01 34 67 90 23 56 89 1234567890123 0',
+    { compact: false, breakLength: 3 });
+  expect = [
+    '\'12 45 78 01 34 \' +',
+    '  \'67 90 23 56 89 \' +',
+    '  \'1234567890123 0\''
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(
+    '12 45 78 01 34 67 90 23 56 89 12345678901234567 0',
+    { compact: false, breakLength: 3 });
+  expect = [
+    '\'12 45 78 01 34 \' +',
+    '  \'67 90 23 56 89 \' +',
+    '  \'12345678901234567 \' +',
+    '  \'0\''
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  o.a = () => {};
+  o.b = new Number(3);
+  out = util.inspect(o, { compact: false, breakLength: 3 });
+  expect = [
+    '{',
+    '  a: [Function],',
+    '  b: [Number: 3]',
+    '}'
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  out = util.inspect(o, { compact: false, breakLength: 3, showHidden: true });
+  expect = [
+    '{',
+    '  a: [Function] {',
+    '    [length]: 0,',
+    "    [name]: ''",
+    '  },',
+    '  b: [Number: 3]',
+    '}'
+  ].join('\n');
+  assert.strictEqual(out, expect);
+
+  o[util.inspect.custom] = () => 42;
+  out = util.inspect(o, { compact: false, breakLength: 3 });
+  expect = '42';
+  assert.strictEqual(out, expect);
+
+  o[util.inspect.custom] = () => '12 45 78 01 34 67 90 23';
+  out = util.inspect(o, { compact: false, breakLength: 3 });
+  expect = '12 45 78 01 34 67 90 23';
+  assert.strictEqual(out, expect);
+
+  o[util.inspect.custom] = () => ({ a: '12 45 78 01 34 67 90 23' });
+  out = util.inspect(o, { compact: false, breakLength: 3 });
+  expect = '{\n  a: \'12 45 78 01 34 \' +\n    \'67 90 23\'\n}';
+  assert.strictEqual(out, expect);
+}