Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: change inspect depth default #17907

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ property take precedence over `--trace-deprecation` and
<!-- YAML
added: v0.5.3
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/17907
description: The `%o` specifiers `depth` option is now set to Infinity.
- version: v8.4.0
pr-url: https://github.com/nodejs/node/pull/14558
description: The `%o` and `%O` specifiers are supported now.
Expand All @@ -209,12 +212,11 @@ corresponding argument. Supported placeholders are:
contains circular references.
* `%o` - Object. A string representation of an object
with generic JavaScript object formatting.
Similar to `util.inspect()` with options `{ showHidden: true, depth: 4, showProxy: true }`.
This will show the full object including non-enumerable symbols and properties.
* `%O` - Object. A string representation of an object
with generic JavaScript object formatting.
Similar to `util.inspect()` without options.
This will show the full object not including non-enumerable symbols and properties.
Similar to `util.inspect()` with options `{ showHidden: true, showProxy: true }`.
This will show the full object including non-enumerable properties and proxies.
* `%O` - Object. A string representation of an object with generic JavaScript
object formatting. Similar to `util.inspect()` without options. This will show
the full object not including non-enumerable properties and proxies.
* `%%` - single percent sign (`'%'`). This does not consume an argument.

If the placeholder does not have a corresponding argument, the placeholder is
Expand Down Expand Up @@ -327,6 +329,9 @@ stream.write('With ES6');
<!-- YAML
added: v0.3.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/17907
description: The `depth` default changed to Infinity.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/REPLACEME
description: The `compact` option is supported now.
Expand All @@ -349,9 +354,6 @@ changes:
* `options` {Object}
* `showHidden` {boolean} If `true`, the `object`'s non-enumerable symbols and
properties will be included in the formatted result. Defaults to `false`.
* `depth` {number} Specifies the number of times to recurse while formatting
the `object`. This is useful for inspecting large complicated objects.
Defaults to `2`. To make it recurse indefinitely pass `null`.
* `colors` {boolean} If `true`, the output will be styled with ANSI color
codes. Defaults to `false`. Colors are customizable, see
[Customizing `util.inspect` colors][].
Expand All @@ -362,8 +364,8 @@ changes:
objects. Defaults to `false`.
* `maxArrayLength` {number} Specifies the maximum number of array and
`TypedArray` elements to include when formatting. Defaults to `100`. Set to
`null` to show all array elements. Set to `0` or negative to show no array
elements.
`null` or `Infinity` to show all array elements. Set to `0` or negative to
show no array elements.
* `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.
Expand All @@ -374,6 +376,10 @@ changes:
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`.
* `depth` {number} Specifies the number visible nested Objects in an `object`.
This is useful to minimize the inspection output for large complicated
objects. To make it recurse indefinitely pass `null` or `Infinity`. Defaults
to `Infinity`.

The `util.inspect()` method returns a string representation of `object` that is
intended for debugging. The output of `util.inspect` may change at any time
Expand All @@ -398,12 +404,23 @@ util.inspect(new Bar()); // 'Bar {}'
util.inspect(baz); // '[foo] {}'
```

The following example inspects all properties of the `util` object:
The following example limits the inspected output of the `paths` property:

```js
const util = require('util');

console.log(util.inspect(util, { showHidden: true, depth: null }));
console.log(util.inspect(module, { depth: 0 }));
// Instead of showing all entries in `paths` `[Array]` is used to limit the
// output for readability:

// Module {
// id: '<repl>',
// exports: {},
// parent: undefined,
// filename: null,
// loaded: false,
// children: [],
// paths: [Array] }
```

Values may supply their own custom `inspect(depth, opts)` functions, when
Expand All @@ -423,7 +440,7 @@ const o = {
'foo']], 4],
b: new Map([['za', 1], ['zb', 'test']])
};
console.log(util.inspect(o, { compact: true, depth: 5, breakLength: 80 }));
console.log(util.inspect(o, { compact: true, breakLength: 80 }));

// This will print

Expand All @@ -437,7 +454,7 @@ console.log(util.inspect(o, { compact: true, depth: 5, breakLength: 80 }));
// 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 }));
console.log(util.inspect(o, { compact: false, breakLength: 80 }));

// {
// a: [
Expand Down
6 changes: 6 additions & 0 deletions lib/internal/streams/BufferList.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const { Buffer } = require('buffer');
const util = require('util');

function copyBuffer(src, target, offset) {
Buffer.prototype.copy.call(src, target, offset);
Expand Down Expand Up @@ -73,4 +74,9 @@ module.exports = class BufferList {
}
return ret;
}

[util.inspect.custom]() {
const obj = util.inspect({ length: this.length });
return `${this.constructor.name} ${obj}`;
}
};
5 changes: 3 additions & 2 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ function hasOwnProperty(obj, prop) {
// Can overridden with custom print functions, such as `probe` or `eyes.js`.
// This is the default "writer" value if none is passed in the REPL options.
const writer = exports.writer = (obj) => util.inspect(obj, writer.options);
writer.options =
Object.assign({}, util.inspect.defaultOptions, { showProxy: true });
writer.options = Object.assign({},
util.inspect.defaultOptions,
{ showProxy: true, depth: 2 });

exports._builtinLibs = internalModule.builtinLibs;

Expand Down
7 changes: 3 additions & 4 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const {

const inspectDefaultOptions = Object.seal({
showHidden: false,
depth: 2,
depth: null,
colors: false,
customInspect: true,
showProxy: false,
Expand Down Expand Up @@ -195,7 +195,7 @@ function format(f) {
break;
case 111: // 'o'
tempStr = inspect(arguments[a++],
{ showHidden: true, depth: 4, showProxy: true });
{ showHidden: true, showProxy: true });
break;
case 105: // 'i'
tempStr = `${parseInt(arguments[a++])}`;
Expand Down Expand Up @@ -317,8 +317,7 @@ Object.defineProperty(inspect, 'defaultOptions', {
if (options === null || typeof options !== 'object') {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object');
}
Object.assign(inspectDefaultOptions, options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for not using _extend?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type check has to be done again in that case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it will add a noticeable overhead but yes it makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Do we actually care about the performance of inspection? I thought we didn’t.
  2. Same concern here as @lpinca. This is quite literally reinventing the wheel, and type checks for object are quite fast in modern V8.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the use _extend as it will still be faster than Object.assign

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the performance - just recently I heard that inspect came up in a flamegraph. Seems like people use it a lot. I am aware that setting the default options will not be a big thing but the first implementation (not changing inspect for console.*) used it a lot and that is why I decided to optimize it as well.

return inspectDefaultOptions;
return _extend(inspectDefaultOptions, options);
}
});

Expand Down
8 changes: 8 additions & 0 deletions test/parallel/test-stream-buffer-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require('../common');
const assert = require('assert');
const BufferList = require('internal/streams/BufferList');
const util = require('util');

// Test empty buffer list.
const emptyList = new BufferList();
Expand All @@ -25,3 +26,10 @@ assert.strictEqual(list.join(','), 'foo');
const shifted = list.shift();
assert.strictEqual(shifted, 'foo');
assert.deepStrictEqual(list, new BufferList());

const tmp = util.inspect.defaultOptions.colors;
util.inspect.defaultOptions = { colors: true };
assert.strictEqual(
util.inspect(list),
'BufferList { length: \u001b[33m0\u001b[39m }');
util.inspect.defaultOptions = { colors: tmp };
16 changes: 10 additions & 6 deletions test/parallel/test-util-inspect-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,17 @@ const expected1 = 'Proxy [ {}, {} ]';
const expected2 = 'Proxy [ Proxy [ {}, {} ], {} ]';
const expected3 = 'Proxy [ Proxy [ Proxy [ {}, {} ], {} ], Proxy [ {}, {} ] ]';
const expected4 = 'Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [ {}, {} ], {} ] ]';
const expected5 = 'Proxy [ Proxy [ Proxy [ Proxy [Array], {} ],' +
const expected5 = 'Proxy [ Proxy [ Proxy [ Proxy [ {}, {} ], {} ],' +
' Proxy [ {}, {} ] ],\n Proxy [ Proxy [ {}, {} ]' +
', Proxy [ Proxy [Array], {} ] ] ]';
const expected6 = 'Proxy [ Proxy [ Proxy [ Proxy [Array], Proxy [Array]' +
' ],\n Proxy [ Proxy [Array], Proxy [Array] ] ],\n' +
' Proxy [ Proxy [ Proxy [Array], Proxy [Array] ],\n' +
' Proxy [ Proxy [Array], Proxy [Array] ] ] ]';
', Proxy [ Proxy [ {}, {} ], {} ] ] ]';
const expected6 = 'Proxy [ Proxy [ Proxy [ Proxy [ Proxy [ {}, {} ], {} ], ' +
'Proxy [ {}, {} ] ],\n' +
' Proxy [ Proxy [ {}, {} ], ' +
'Proxy [ Proxy [ {}, {} ], {} ] ] ],\n' +
' Proxy [ Proxy [ Proxy [ Proxy [ {}, {} ], {} ], ' +
'Proxy [ {}, {} ] ],\n' +
' Proxy [ Proxy [ {}, {} ], ' +
'Proxy [ Proxy [ {}, {} ], {} ] ] ] ]';
assert.strictEqual(
util.inspect(proxy1, { showProxy: true, depth: null }),
expected1);
Expand Down
26 changes: 13 additions & 13 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ assert.strictEqual(util.inspect({ a: 1, b: 2 }), '{ a: 1, b: 2 }');
assert.strictEqual(util.inspect({ 'a': {} }), '{ a: {} }');
assert.strictEqual(util.inspect({ 'a': { 'b': 2 } }), '{ a: { b: 2 } }');
assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }),
'{ a: { b: { c: [Object] } } }');
'{ a: { b: { c: { d: 2 } } } }');
assert.strictEqual(
util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }, false, null),
'{ a: { b: { c: { d: 2 } } } }');
Expand Down Expand Up @@ -106,7 +106,7 @@ assert.strictEqual(util.inspect((new JSStream())._externalStream),
assert.strictEqual(util.inspect({ a: regexp }, false, 0), '{ a: /regexp/ }');
}

assert(/Object/.test(
assert(!/Object/.test(
util.inspect({ a: { a: { a: { a: {} } } } }, undefined, undefined, true)
));
assert(!/Object/.test(
Expand Down Expand Up @@ -1012,15 +1012,15 @@ if (typeof Symbol !== 'undefined') {
// Empty and circular before depth
{
const arr = [[[[]]]];
assert.strictEqual(util.inspect(arr), '[ [ [ [] ] ] ]');
assert.strictEqual(util.inspect(arr, { depth: 2 }), '[ [ [ [] ] ] ]');
arr[0][0][0][0] = [];
assert.strictEqual(util.inspect(arr), '[ [ [ [Array] ] ] ]');
assert.strictEqual(util.inspect(arr, { depth: 2 }), '[ [ [ [Array] ] ] ]');
arr[0][0][0] = {};
assert.strictEqual(util.inspect(arr), '[ [ [ {} ] ] ]');
assert.strictEqual(util.inspect(arr, { depth: 2 }), '[ [ [ {} ] ] ]');
arr[0][0][0] = { a: 2 };
assert.strictEqual(util.inspect(arr), '[ [ [ [Object] ] ] ]');
assert.strictEqual(util.inspect(arr, { depth: 2 }), '[ [ [ [Object] ] ] ]');
arr[0][0][0] = arr;
assert.strictEqual(util.inspect(arr), '[ [ [ [Circular] ] ] ]');
assert.strictEqual(util.inspect(arr, { depth: 2 }), '[ [ [ [Circular] ] ] ]');
}

// Corner cases.
Expand Down Expand Up @@ -1117,22 +1117,22 @@ if (typeof Symbol !== 'undefined') {
assert(!/1 more item/.test(util.inspect(arr)));
util.inspect.defaultOptions.maxArrayLength = oldOptions.maxArrayLength;
assert(/1 more item/.test(util.inspect(arr)));
util.inspect.defaultOptions.depth = null;
assert(!/Object/.test(util.inspect(obj)));
util.inspect.defaultOptions.depth = oldOptions.depth;
util.inspect.defaultOptions.depth = 2;
assert(/Object/.test(util.inspect(obj)));
util.inspect.defaultOptions.depth = oldOptions.depth;
assert(!/Object/.test(util.inspect(obj)));
assert.strictEqual(
JSON.stringify(util.inspect.defaultOptions),
JSON.stringify(oldOptions)
);

// Set multiple options through object assignment
util.inspect.defaultOptions = { maxArrayLength: null, depth: null };
util.inspect.defaultOptions = { maxArrayLength: null, depth: 2 };
assert(!/1 more item/.test(util.inspect(arr)));
assert(!/Object/.test(util.inspect(obj)));
assert(/Object/.test(util.inspect(obj)));
util.inspect.defaultOptions = oldOptions;
assert(/1 more item/.test(util.inspect(arr)));
assert(/Object/.test(util.inspect(obj)));
assert(!/Object/.test(util.inspect(obj)));
assert.strictEqual(
JSON.stringify(util.inspect.defaultOptions),
JSON.stringify(oldOptions)
Expand Down