Skip to content

console: add dirxml method #17152

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

Closed
wants to merge 3 commits into from
Closed

Conversation

Tiriel
Copy link
Contributor

@Tiriel Tiriel commented Nov 20, 2017

This is an absolute first second draft.
This method was previously exposed by V8 (since Node v8.0.0) and not
implemented in Node directly.
Tests coming soon.

Refs: #17128

Please feel free to comment and give any advice!

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

console

@nodejs-github-bot nodejs-github-bot added the console Issues and PRs related to the console subsystem. label Nov 20, 2017
lib/console.js Outdated
@@ -162,6 +162,25 @@ Console.prototype.dir = function dir(object, options) {
};


Console.prototype.dirxml = function dirxml(...data) {
const optionProps = ['showHidden', 'depth', 'colors'],
maybeOptions = Object.getOwnPropertyNames(data.slice(-1)),
Copy link
Contributor

Choose a reason for hiding this comment

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

These should be separate const statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted!

lib/console.js Outdated
Console.prototype.dirxml = function dirxml(...data) {
const optionProps = ['showHidden', 'depth', 'colors'],
maybeOptions = Object.getOwnPropertyNames(data.slice(-1)),
isOption = maybeOptions.some((p) => optionProps.indexOf(p) !== -1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the last argument supposed to be an options object? The spec doesn't seem to call for that, and it makes for a slightly awkward UX, IMO. What if we want to display an object that has one of those magic props?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't get why this is here. The spec doesn't allow to specify options for dirxml. The fact that there has to be this ugly code to detect it should indicate that it should not exist in the first place.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's kind of hard said like that.

Anyway, I thought about the possibility of having the last object having precisely one of those options, but I thought that it would be better to allow passing options if need be, just like console.dir() and that the risk would be quite minimal.
Plus, it feels a bit weird to have dir accept options but take only one object, and dirxml not accepting options but taking a varying number of object, but maybe that's just me.
And it does remove the possibility for custom inspect, and so xml redering.

I'll remove it for now anyway, since it seems to be a bad idea, it's indeed akward.

Copy link
Contributor

Choose a reason for hiding this comment

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

dir explicitly lists options as something that it accepts in the spec: https://console.spec.whatwg.org/#dir

When working on features which have a spec, decisions should always be informed by the behaviour defined there.

Copy link
Contributor

@apapirovski apapirovski Nov 20, 2017

Choose a reason for hiding this comment

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

Also, since the console spec is an early draft that's classified as a Living Standard, there's always the opportunity for you to go back and try to suggest improvement at the source. But Node.js shouldn't be the origin for that divergence since it just creates confusion for developers.

Copy link
Member

Choose a reason for hiding this comment

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

BTW, the console.dir spec was in fact amended to account for the possibility of having an options object because of Node.js (see whatwg/console#79). However, I agree with @apapirovski that

Node.js shouldn't be the origin for that divergence since it just creates confusion for developers.

-- not if we can help it anyway, which we couldn't with console.dir w/o breaking backwards compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, definitely agree and good to have a bit of background on that! Thanks @TimothyGu.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the background indeed!

However, I don't know if dir is widely used, but I don't know either if it would be worth it to go and ask if the standard could be adapted.
Any opinion on this?

Still dropping this while the standard is as its current state anyway, it does make sense, thank you all!

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem in this particular case is that dirxml accepts a rest parameter which always has to be final, it's similar in that to log and most other console functions.

There would need to either be a method to set general settings for all dirxml calls or some options object that is exposed on the console, or some sort of a Symbol (similar to like @@toStringTag) that gets declared on the object that decides the formatting preferences.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed that's a problem, that's what had me attempt (poorly) to parse the last object in the list. I don't mind attempting again if the standard does evolve though. If it evolves and if it's worth it. I don't know if it's the case though.

@tniessen
Copy link
Member

First of all, thank you, @Tiriel!

I was a bit surprised to see this implemented, the MDN page still says:

This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future. [...] Displays an interactive tree of the descendant elements of the specified XML/HTML element. [...] The output is presented as a hierarchical listing of expandable nodes that let you see the contents of child nodes.

If that was still the case, I would generally be -1 on implementing this. Even if it has been included in a WhatWG living standard, we are not required to implement it. I have barely ever seen someone use dirxml(), and Firefox only recently updated its implementation according to WhatWG (older versions simply displayed empty XML tags if the contents were not DOM nodes).

If we are implementing this for the sake of conformity with the WhatWG standard, I am okay with this. Apart from that, I am not a fan of the function itself, especially in a non-interactive environment such as the Node.js console. Secondly, the name is misleading in an environment which does not have a concept of XML / HTML.

The standard is vague, but the main aspect of the function is this:

Let converted be a DOM tree representation of item if possible; otherwise let converted be item with optimally useful formatting applied.

We would basically use the fallback in all cases, which is no different than other console functions.

@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 20, 2017

@tniessen Thanks for the input!

I am actually trying to implement it for conformity with the living standard indeed, as was (very lightly) discussed #17128

I totally agree with you on the use of the fallback though. I tried adding the possibility to pass a custom inspect function, to return proper xml when needed, but was dismissed for quite good reasons ( #17146 ). It would then in effect be really similar to console.dir().

The problem is, we've seen at least two times (need to find again the issues numbers, but at least #16755 ) people searching for the console methods exposed by V8. After discussing a bit with some people, I thought it would be better to have at least a minimal implementation of our own of these methods, or at least the ones present in the Living Standard.

As said in other comments though, if the standard can be made to change (I quite don't understand why they allow passing options in dir but not in dirxml), I'd be happy to improve my implementation. I'm just a bit afraid to ask for these changes, and agree we probably shouldn't be the ones to ask for them.

Hope this makes sense!

@Tiriel Tiriel force-pushed the console-dirxml-draft branch from e362fe5 to a69828a Compare November 22, 2017 21:56
@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 22, 2017

Hi everyone!

I just reworked the function to simply call console.dir for each argument received, as a minimal implementation following the standard.

As said, this implementation is for the sake of conformity until something better can be found, and to avoid having people trying to call the V8 exposed methods with no results.

Documentation and test updated accordingly.

Hope it's ok for everyone!

@Tiriel Tiriel force-pushed the console-dirxml-draft branch 2 times, most recently from e5d5898 to 4349856 Compare November 22, 2017 22:05

This method calls `console.dir()` with default options for each argument it
receives. See [`console.dir()`][] for more details about said defaults.
Please note that this method doesn't produce any xml formatting and uses the
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Please use does not instead of the abbreviated doesn't.

Copy link
Member

Choose a reason for hiding this comment

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

Nit: xmlXML. When in doubt, check the specification.

This method calls `console.dir()` with default options for each argument it
receives. See [`console.dir()`][] for more details about said defaults.
Please note that this method doesn't produce any xml formatting and uses the
default `console.dir()` formatting resolution instead.
Copy link
Member

Choose a reason for hiding this comment

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

Not a native English speaker myself, but resolution sounds strange here IMO (at least I never used it in this context). It is not really necessary anyway as the first sentence already has the same information.

Copy link
Contributor Author

@Tiriel Tiriel Nov 23, 2017

Choose a reason for hiding this comment

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

Well, I'll just let the precision on the absence of XML formatting then, just to be sure it's clear for everyone. But I thought resolution was indicated, you have me doubting now 😄

lib/console.js Outdated
@@ -162,6 +162,13 @@ Console.prototype.dir = function dir(object, options) {
};


Console.prototype.dirxml = function dirxml(...data) {
for (const item of data) {
Console.prototype.dir.call(this, item);
Copy link
Member

Choose a reason for hiding this comment

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

As far as I know, console functions must not be called without a proper context, so this.dir(item) should work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll just check this and make sure it works! Thanks!

"{ foo: 'bar', inspect: [Function: inspect] }\n");
assert.strictEqual(strings.shift().includes('foo: { bar: { baz:'), true);
assert.strictEqual(strings.shift().includes('quux'), true);
assert.strictEqual(strings.shift().includes('quux: true'), true);
Copy link
Member

Choose a reason for hiding this comment

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

Please do not use assert.strictEqual(..., true). Just use assert(...) (or assert.ok(...)) to test for logic values. You can find the documentation here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't know, sorry. Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

No problem, thanks for going through our (sometimes a little long) process!

@tniessen
Copy link
Member

Again, thanks for working on this! 😃

I still can't say I like this function or seeing it implemented / used in non-browser environments, but if we consider the WhatWG standard important enough to go by it[1], I would like to point out that I personally think the standard suggests to print the items the same way console.log() would print them if they were passed as arguments after preprocessing.

In Firefox 57:
firefox

In Chrome 58:
chrome

Node.js 8.9 with your patch:
node

I am not saying one behavior is better than the other or that I would personally prefer either, I am just wondering whether it would be a good idea to stick to conventions here. (The standard is really vague.)


[1] As long as it contains a big red box saying "TODO: This will need a good algorithm" I would not necessarily do that.

Copy link
Contributor

@cjihrig cjihrig left a comment

Choose a reason for hiding this comment

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

LGTM with nits addressed.

@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 23, 2017

I have a commit ready to be pushed that adresses the nits in doc, but I am on another computer and need to build node first for testing/linting.

Anyway, the commit I want to push also changes the method from parsing args and using console.dir to simply logging using console.log as suggested by @tniessen . Is there consensus on this evolution or do I leave the method as it is in the last commit pushed?

@TimothyGu
Copy link
Member

Seems like @tniessen is right about using dir vs. log: the spec calls for "optimally useful formatting", which is equivalent to log while dir uses "generic JavaScript object formatting." @Tiriel your changes sound good to me!

@Tiriel Tiriel force-pushed the console-dirxml-draft branch from 4349856 to 335896d Compare November 23, 2017 19:45
@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 23, 2017

Here we are! @tniessen @TimothyGu @cjihrig PTAL!

And thank you all!

* `...data` {any}

This method calls `console.dir()` with default options for each argument it
receives. See [`console.dir()`][] for more details about said defaults.
Copy link
Member

Choose a reason for hiding this comment

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

This will require some changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Damn, sorry about that. Fixing right now and squashing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

@Tiriel Tiriel force-pushed the console-dirxml-draft branch from 335896d to 68d62dd Compare November 23, 2017 20:51
added: v8.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/17128
Copy link
Member

Choose a reason for hiding this comment

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

Nit: This should be https://github.com/nodejs/node/pull/17152 as far as I can tell.

lib/console.js Outdated
@@ -162,6 +162,11 @@ Console.prototype.dir = function dir(object, options) {
};


Console.prototype.dirxml = function dirxml(...data) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: This can be simplified to Console.prototype.dirxml = Console.prototype.log as far as I can tell.

This method was previously exposed by V8 (since Node v8.0.0) and not
implemented in Node directly.
Tests coming soon.

Refs: nodejs#17128
…eceived.

Minimal implementation following the Living Standard specs, following
reviews.

Fixes: nodejs#17128
Nits in documentation, rework dirxml to use console.log, tests.

Fixes: nodejs#17128
@Tiriel Tiriel force-pushed the console-dirxml-draft branch from 68d62dd to a8ea202 Compare November 24, 2017 09:26
@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 24, 2017

Nits adressed! Thanks @tniessen

Branch can be merge, but I'll keep a tab to see if some basic XML formatting can be implemented someday in the inspection functions. I'm quite not satisfied with this method being just another alias for log, although it allows a basic implementation for now.

@apapirovski
Copy link
Contributor

Branch can be merge, but I'll keep a tab to see if some basic XML formatting can be implemented someday in the inspection functions.

I feel like the naming might be throwing you off. dirxml is supposed to provide a DOM tree representation of a DOM object, it shouldn't really do anything with XML. Since Node doesn't have any sort of a concept of a DOM, there won't ever be need for it to do more than it does currently.

@tniessen
Copy link
Member

What @apapirovski described is true. The function was designed for browsers, that's why the WhatWG living standard focuses on DOM / XML here, but there really is no concept of that within node. Not even browsers format non-DOM nodes as XML:

firefox

@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 24, 2017

@apapirovski @tniessen I admit at first I have been quite confused and thought it should parse XML, but that's not what I'm talking about now.

The Standard isn't very clear, but it seems to indicate that dirxml should try and convert arguments it receives to display them as XML:

For each item of data:

Let converted be a DOM tree representation of item if possible; otherwise let converted be item with optimally useful formatting applied.

The term "converted" at least seems to indicate that to me. I know Node doesn't implement DOM, so it would be some "display only" thing. And I know browsers don't do that either, but as said, the standard isn't very clear and there are know differences between the standard and the browsers implementations (see the discussion about console.debug).

Anyway as said, it can be merged in this state, it does fulfill the "otherwise" condition above, it's just something I want to investigate on my own afterwards ^^

Thanks again for all the inputs!

@tniessen
Copy link
Member

but it seems to indicate that dirxml should try and convert arguments it receives to display them as XML

I am not an expert here, but as far as I know, DOM is basically an interpretation of HTML/XML documents, and not every XML document / tree produces a DOM tree (even though every DOM tree can be represented as an XML document). So even if you formatted the data as XML, that still would not make it a "DOM tree representation". I don't think browsers will attempt to display data as DOM trees unless they are DOM nodes. Why would anyone want to have a DOM representation of data which is not DOM?

Anyway, feel free to research this yourself! :)

@evanlucas
Copy link
Contributor

I'm not sure it makes sense to add this to console, especially if it doesn't output in XML. Although I greatly appreciate the time you took to work on this @Tiriel I think I'm -1.

@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 24, 2017

@devsnek Well, I don't know how exactly they implement it, but that's the way it is reduced in this PR, and I think that should be the strict minimum implemented, if only to expose it on our own and respect the standard.

I'd like it to present a version of DOM treeish printing, even if it can't be interactive like in the browsers for obvious reasons, but I'll settle for the console.log alias.

@apapirovski
Copy link
Contributor

apapirovski commented Nov 24, 2017

Node core has no concept of DOM... We can't print a DOM tree when we don't know what a DOM node is and isn't. Browsers have specific APIs around the DOM that are not in Node and that are instead re-created by packages like jsdom.

Countless Node apps may receive datas from API dealing XML, or parse DOM documents for various reasons (modifying templates before sending them, or else)

Node apps do not deal with DOM unless they implement jsdom or similar. They're usually dealing either with Virtual DOM (React & similar), or they're dealing with strings that represent HTML/XML.

Node is used for Front apps, at least as much as in backend.

Node is not used for front-end apps. It's used for front-end tooling, which again has no concept of the DOM unless it implements jsdom or similar.

That we don't want to implement the DOM, I can understand, I can only imagine the tremendous amount of work it would require, looking at the packages on npm like jsdom. But we could at least display DOM elements as a pseudo tree, it would make debuging much easier.

See above. We can't display something we don't have any concept of.


To elaborate, most of the core JS implementation in Node.js comes from V8 with certain other APIs implemented by Node instead (setTimeout, TextDecoder, etc.). The DOM (and, more specifically, its implementation) is a wholly separate thing that, within Chrome, is connected to V8 via bindings and that Node.js does not currently have access to nor any hope of having access to.

@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 24, 2017

@apapirovski Even if that's the case - and I know it is, you're perfectly right - that's still a problem I'll deal with when searching possible solutions. But that's not the purpose of this PR anymore since console.dirxml is presented as a console.logalias.


I've noted that at least one person -1.
I'd like a formal dismissal if the majority thinks it is dumb to merge this PR. I've laid down my arguments for the implementation of a treeish view in this comment #17152 (comment) , but they are quite the same for the alias implementation. Ithink this is quite a minor change that would still allow for a slightly more user friendly experience.

@apapirovski
Copy link
Contributor

apapirovski commented Nov 24, 2017

I'm trying to save you time by explaining why there are not "possible solutions" to the problem you think that there is. This either lands as a simple alias or it won't land at all, there's no better implementation that Node needs or — more importantly — can possibly have. I'm simply trying to avoid a situation where you spend time writing an XML parser or Object to tree formatter, only to have that PR be rejected.

@Tiriel
Copy link
Contributor Author

Tiriel commented Nov 24, 2017

@apapirovski I do appreciate that, thanks sincerely. But everyone needs a hobby ;)
And in any case, that won't be done in this PR, I intend on leaving it like that. Exactly like you said, it either lands is it is now, or doesn't land at all.

And while I'm at it, I hope I offended no one, it was not my intent. I'm not a native english speaker, I make mistakes, but I do try my best to express myself correctly.

Copy link
Contributor

@apapirovski apapirovski left a comment

Choose a reason for hiding this comment

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

For the record, I think this should land as is. There's no possibility of getting something better and this can potentially allow code re-use in some bizarre code bases that rely on this and don't use the DOM...

@tniessen
Copy link
Member

It's what I have been saying all along, this function does not make sense in Node.js core, and the only reason to implement it is conformity with the WhatWG living standard. Personally, I don't deem conformity with a half-baked living standard important, but I also won't block this because apparently, others do. I think it won't get any better than this implementation and it does not make things worse than they currently are, except that implementing and documenting it means that people will eventually start using it, and that's when the trouble starts. At the moment, we are kind of at advantage as the standard is vague enough to allow dirxml to be an alias for log.

@evanlucas Please make your -1 explicit if you feel like this should not land.

I'd suggest to keep this open for a couple more days either way so we get a chance to get more feedback from the team. I think @jasnell worked on the console for some time, maybe he has some insights.

Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

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

I'm fine with this strictly as an alias for console.log.

@tniessen tniessen self-assigned this Dec 3, 2017
@tniessen
Copy link
Member

tniessen commented Dec 3, 2017

I would like to land this within the next couple of days. Are you okay with that, @evanlucas?

@evanlucas
Copy link
Contributor

Yea I don’t want to block this if others want this in. Thanks!

@tniessen
Copy link
Member

tniessen commented Dec 3, 2017

@tniessen
Copy link
Member

tniessen commented Dec 3, 2017

Landed in c84710d.

@tniessen tniessen closed this Dec 3, 2017
tniessen pushed a commit that referenced this pull request Dec 3, 2017
This method was previously exposed by V8 (since node 8.0.0) but not
implemented in node.

PR-URL: #17152
Refs: #17128
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Tobias Nießen <[email protected]>
Reviewed-By: Anatoli Papirovski <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this pull request Dec 12, 2017
This method was previously exposed by V8 (since node 8.0.0) but not
implemented in node.

PR-URL: #17152
Refs: #17128
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Tobias Nießen <[email protected]>
Reviewed-By: Anatoli Papirovski <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this pull request Dec 12, 2017
This method was previously exposed by V8 (since node 8.0.0) but not
implemented in node.

PR-URL: #17152
Refs: #17128
Reviewed-By: Timothy Gu <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Tobias Nießen <[email protected]>
Reviewed-By: Anatoli Papirovski <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@MylesBorins MylesBorins mentioned this pull request Dec 12, 2017
@gibfahn
Copy link
Member

gibfahn commented Dec 20, 2017

Shouldn't this be semver-minor?

@gibfahn gibfahn added semver-minor PRs that contain new features and should be released in the next minor version. lts-watch-v6.x labels Dec 20, 2017
@Trott
Copy link
Member

Trott commented Dec 20, 2017

Shouldn't this be semver-minor?

@gibfahn Depends on your perspective. Previously, the function was exposed (due to the inspector implementation) but was a no-op. Are we adding functionality or fixing something that's broken?

I could go either way, but think semver-minor is probably better (if for no other reason than the "when in doubt, use the bigger change indicator" maxim).

@Tiriel
Copy link
Contributor Author

Tiriel commented Dec 20, 2017

I know I'm not TSC, but as the OP, if I may just say a word...
Seeing that the other method implemented with this logic, console.debug, landed in the same v9 semver-minor, and that it's marked as semver-minor for lts (per #17033 (comment) ) I'd say there's a good point in making this one semver-minor too.

@gibfahn
Copy link
Member

gibfahn commented Dec 20, 2017

Thanks @Trott and @Tiriel

I know I'm not TSC, but as the OP, if I may just say a word...

My opinion is that the more time you've spent on something the more weight your opinion has. Given the work you've done on this PR, your view is definitely useful. Thanks for replying!

antonPiPx pushed a commit to antonPiPx/node that referenced this pull request Aug 7, 2025
# Console

<!--introduced_in=v0.10.13-->

> Stability: 2 - Stable

<!-- source_link=lib/console.js -->

The `node:console` module provides a simple debugging console that is similar to
the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

* A `Console` class with methods such as `console.log()`, `console.error()`, and
  `console.warn()` that can be used to write to any Node.js stream.
* A global `console` instance configured to write to [`process.stdout`][] and
  [`process.stderr`][]. The global `console` can be used without calling
  `require('node:console')`.

_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. Programs that desire to depend
on the synchronous / asynchronous behavior of the console functions should
first figure out the nature of console's backing stream. This is because the
stream is dependent on the underlying platform and standard stream
configuration of the current process. See the [note on process I/O][] for
more information.

Example using the global `console`:

```js
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
```

Example using the `Console` class:

```js
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
```

## Class: `Console`

<!-- YAML
changes:
  - version: v8.0.0
    pr-url: nodejs#9744
    description: Errors that occur while writing to the underlying streams
                 will now be ignored by default.
-->

<!--type=class-->

The `Console` class can be used to create a simple logger with configurable
output streams and can be accessed using either `require('node:console').Console`
or `console.Console` (or their destructured counterparts):

```mjs
import { Console } from 'node:console';
```

```cjs
const { Console } = require('node:console');
```

```js
const { Console } = console;
```

### `new Console(stdout[, stderr][, ignoreErrors])`

### `new Console(options)`

<!-- YAML
changes:
  - version:
     - v14.2.0
     - v12.17.0
    pr-url: nodejs#32964
    description: The `groupIndentation` option was introduced.
  - version: v11.7.0
    pr-url: nodejs#24978
    description: The `inspectOptions` option is introduced.
  - version: v10.0.0
    pr-url: nodejs#19372
    description: The `Console` constructor now supports an `options` argument,
                 and the `colorMode` option was introduced.
  - version: v8.0.0
    pr-url: nodejs#9744
    description: The `ignoreErrors` option was introduced.
-->

* `options` {Object}
  * `stdout` {stream.Writable}
  * `stderr` {stream.Writable}
  * `ignoreErrors` {boolean} Ignore errors when writing to the underlying
    streams. **Default:** `true`.
  * `colorMode` {boolean|string} Set color support for this `Console` instance.
    Setting to `true` enables coloring while inspecting values. Setting to
    `false` disables coloring while inspecting values. Setting to
    `'auto'` makes color support depend on the value of the `isTTY` property
    and the value returned by `getColorDepth()` on the respective stream. This
    option can not be used, if `inspectOptions.colors` is set as well.
    **Default:** `'auto'`.
  * `inspectOptions` {Object} Specifies options that are passed along to
    [`util.inspect()`][].
  * `groupIndentation` {number} Set group indentation.
    **Default:** `2`.

Creates a new `Console` with one or two writable stream instances. `stdout` is a
writable stream to print log or info output. `stderr` is used for warning or
error output. If `stderr` is not provided, `stdout` is used for `stderr`.

```mjs
import { createWriteStream } from 'node:fs';
import { Console } from 'node:console';
// Alternatively
// const { Console } = console;

const output = createWriteStream('./stdout.log');
const errorOutput = createWriteStream('./stderr.log');
// Custom simple logger
const logger = new Console({ stdout: output, stderr: errorOutput });
// use it like console
const count = 5;
logger.log('count: %d', count);
// In stdout.log: count 5
```

```cjs
const fs = require('node:fs');
const { Console } = require('node:console');
// Alternatively
// const { Console } = console;

const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
// Custom simple logger
const logger = new Console({ stdout: output, stderr: errorOutput });
// use it like console
const count = 5;
logger.log('count: %d', count);
// In stdout.log: count 5
```

The global `console` is a special `Console` whose output is sent to
[`process.stdout`][] and [`process.stderr`][]. It is equivalent to calling:

```js
new Console({ stdout: process.stdout, stderr: process.stderr });
```

### `console.assert(value[, ...message])`

<!-- YAML
added: v0.1.101
changes:
  - version: v10.0.0
    pr-url: nodejs#17706
    description: The implementation is now spec compliant and does not throw
                 anymore.
-->

* `value` {any} The value tested for being truthy.
* `...message` {any} All arguments besides `value` are used as error message.

`console.assert()` writes a message if `value` is [falsy][] or omitted. It only
writes a message and does not otherwise affect execution. The output always
starts with `"Assertion failed"`. If provided, `message` is formatted using
[`util.format()`][].

If `value` is [truthy][], nothing happens.

```js
console.assert(true, 'does nothing');

console.assert(false, 'Whoops %s work', 'didn\'t');
// Assertion failed: Whoops didn't work

console.assert();
// Assertion failed
```

### `console.clear()`

<!-- YAML
added: v8.3.0
-->

When `stdout` is a TTY, calling `console.clear()` will attempt to clear the
TTY. When `stdout` is not a TTY, this method does nothing.

The specific operation of `console.clear()` can vary across operating systems
and terminal types. For most Linux operating systems, `console.clear()`
operates similarly to the `clear` shell command. On Windows, `console.clear()`
will clear only the output in the current terminal viewport for the Node.js
binary.

### `console.count([label])`

<!-- YAML
added: v8.3.0
-->

* `label` {string} The display label for the counter. **Default:** `'default'`.

Maintains an internal counter specific to `label` and outputs to `stdout` the
number of times `console.count()` has been called with the given `label`.

<!-- eslint-skip -->

```js
> console.count()
default: 1
undefined
> console.count('default')
default: 2
undefined
> console.count('abc')
abc: 1
undefined
> console.count('xyz')
xyz: 1
undefined
> console.count('abc')
abc: 2
undefined
> console.count()
default: 3
undefined
>
```

### `console.countReset([label])`

<!-- YAML
added: v8.3.0
-->

* `label` {string} The display label for the counter. **Default:** `'default'`.

Resets the internal counter specific to `label`.

<!-- eslint-skip -->

```js
> console.count('abc');
abc: 1
undefined
> console.countReset('abc');
undefined
> console.count('abc');
abc: 1
undefined
>
```

### `console.debug(data[, ...args])`

<!-- YAML
added: v8.0.0
changes:
  - version: v8.10.0
    pr-url: nodejs#17033
    description: "`console.debug` is now an alias for `console.log`."
-->

* `data` {any}
* `...args` {any}

The `console.debug()` function is an alias for [`console.log()`][].

### `console.dir(obj[, options])`

<!-- YAML
added: v0.1.101
-->

* `obj` {any}
* `options` {Object}
  * `showHidden` {boolean} If `true` then the object's non-enumerable and symbol
    properties will be shown too. **Default:** `false`.
  * `depth` {number} Tells [`util.inspect()`][] how many times to recurse while
    formatting the object. This is useful for inspecting large complicated
    objects. To make it recurse indefinitely, pass `null`. **Default:** `2`.
  * `colors` {boolean} If `true`, then the output will be styled with ANSI color
    codes. Colors are customizable;
    see [customizing `util.inspect()` colors][]. **Default:** `false`.

Uses [`util.inspect()`][] on `obj` and prints the resulting string to `stdout`.
This function bypasses any custom `inspect()` function defined on `obj`.

### `console.dirxml(...data)`

<!-- YAML
added: v8.0.0
changes:
  - version: v9.3.0
    pr-url: nodejs#17152
    description: "`console.dirxml` now calls `console.log` for its arguments."
-->

* `...data` {any}

This method calls `console.log()` passing it the arguments received.
This method does not produce any XML formatting.

### `console.error([data][, ...args])`

<!-- YAML
added: v0.1.100
-->

* `data` {any}
* `...args` {any}

Prints to `stderr` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3) (the arguments are all passed to
[`util.format()`][]).

```js
const code = 5;
console.error('error #%d', code);
// Prints: error nodejs#5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
```

If formatting elements (e.g. `%d`) are not found in the first string then
[`util.inspect()`][] is called on each argument and the resulting string
values are concatenated. See [`util.format()`][] for more information.

### `console.group([...label])`

<!-- YAML
added: v8.5.0
-->

* `...label` {any}

Increases indentation of subsequent lines by spaces for `groupIndentation`
length.

If one or more `label`s are provided, those are printed first without the
additional indentation.

### `console.groupCollapsed()`

<!-- YAML
  added: v8.5.0
-->

An alias for [`console.group()`][].

### `console.groupEnd()`

<!-- YAML
added: v8.5.0
-->

Decreases indentation of subsequent lines by spaces for `groupIndentation`
length.

### `console.info([data][, ...args])`

<!-- YAML
added: v0.1.100
-->

* `data` {any}
* `...args` {any}

The `console.info()` function is an alias for [`console.log()`][].

### `console.log([data][, ...args])`

<!-- YAML
added: v0.1.100
-->

* `data` {any}
* `...args` {any}

Prints to `stdout` with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3) (the arguments are all passed to
[`util.format()`][]).

```js
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
```

See [`util.format()`][] for more information.

### `console.table(tabularData[, properties])`

<!-- YAML
added: v10.0.0
-->

* `tabularData` {any}
* `properties` {string\[]} Alternate properties for constructing the table.

Try to construct a table with the columns of the properties of `tabularData`
(or use `properties`) and rows of `tabularData` and log it. Falls back to just
logging the argument if it can't be parsed as tabular.

```js
// These can't be parsed as tabular data
console.table(Symbol());
// Symbol()

console.table(undefined);
// undefined

console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]);
// ┌─────────┬─────┬─────┐
// │ (index) │ a   │ b   │
// ├─────────┼─────┼─────┤
// │ 0       │ 1   │ 'Y' │
// │ 1       │ 'Z' │ 2   │
// └─────────┴─────┴─────┘

console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']);
// ┌─────────┬─────┐
// │ (index) │ a   │
// ├─────────┼─────┤
// │ 0       │ 1   │
// │ 1       │ 'Z' │
// └─────────┴─────┘
```

### `console.time([label])`

<!-- YAML
added: v0.1.104
-->

* `label` {string} **Default:** `'default'`

Starts a timer that can be used to compute the duration of an operation. Timers
are identified by a unique `label`. Use the same `label` when calling
[`console.timeEnd()`][] to stop the timer and output the elapsed time in
suitable time units to `stdout`. For example, if the elapsed
time is 3869ms, `console.timeEnd()` displays "3.869s".

### `console.timeEnd([label])`

<!-- YAML
added: v0.1.104
changes:
  - version: v13.0.0
    pr-url: nodejs#29251
    description: The elapsed time is displayed with a suitable time unit.
  - version: v6.0.0
    pr-url: nodejs#5901
    description: This method no longer supports multiple calls that don't map
                 to individual `console.time()` calls; see below for details.
-->

* `label` {string} **Default:** `'default'`

Stops a timer that was previously started by calling [`console.time()`][] and
prints the result to `stdout`:

```js
console.time('bunch-of-stuff');
// Do a bunch of stuff.
console.timeEnd('bunch-of-stuff');
// Prints: bunch-of-stuff: 225.438ms
```

### `console.timeLog([label][, ...data])`

<!-- YAML
added: v10.7.0
-->

* `label` {string} **Default:** `'default'`
* `...data` {any}

For a timer that was previously started by calling [`console.time()`][], prints
the elapsed time and other `data` arguments to `stdout`:

```js
console.time('process');
const value = expensiveProcess1(); // Returns 42
console.timeLog('process', value);
// Prints "process: 365.227ms 42".
doExpensiveProcess2(value);
console.timeEnd('process');
```

### `console.trace([message][, ...args])`

<!-- YAML
added: v0.1.104
-->

* `message` {any}
* `...args` {any}

Prints to `stderr` the string `'Trace: '`, followed by the [`util.format()`][]
formatted message and stack trace to the current position in the code.

```js
console.trace('Show me');
// Prints: (stack trace will vary based on where trace is called)
//  Trace: Show me
//    at repl:2:9
//    at REPLServer.defaultEval (repl.js:248:27)
//    at bound (domain.js:287:14)
//    at REPLServer.runBound [as eval] (domain.js:300:12)
//    at REPLServer.<anonymous> (repl.js:412:12)
//    at emitOne (events.js:82:20)
//    at REPLServer.emit (events.js:169:7)
//    at REPLServer.Interface._onLine (readline.js:210:10)
//    at REPLServer.Interface._line (readline.js:549:8)
//    at REPLServer.Interface._ttyWrite (readline.js:826:14)
```

### `console.warn([data][, ...args])`

<!-- YAML
added: v0.1.100
-->

* `data` {any}
* `...args` {any}

The `console.warn()` function is an alias for [`console.error()`][].

## Inspector only methods

The following methods are exposed by the V8 engine in the general API but do
not display anything unless used in conjunction with the [inspector][]
(`--inspect` flag).

### `console.profile([label])`

<!-- YAML
added: v8.0.0
-->

* `label` {string}

This method does not display anything unless used in the inspector. The
`console.profile()` method starts a JavaScript CPU profile with an optional
label until [`console.profileEnd()`][] is called. The profile is then added to
the **Profile** panel of the inspector.

```js
console.profile('MyLabel');
// Some code
console.profileEnd('MyLabel');
// Adds the profile 'MyLabel' to the Profiles panel of the inspector.
```

### `console.profileEnd([label])`

<!-- YAML
added: v8.0.0
-->

* `label` {string}

This method does not display anything unless used in the inspector. Stops the
current JavaScript CPU profiling session if one has been started and prints
the report to the **Profiles** panel of the inspector. See
[`console.profile()`][] for an example.

If this method is called without a label, the most recently started profile is
stopped.

### `console.timeStamp([label])`

<!-- YAML
added: v8.0.0
-->

* `label` {string}

This method does not display anything unless used in the inspector. The
`console.timeStamp()` method adds an event with the label `'label'` to the
**Timeline** panel of the inspector.

[`console.error()`]: #consoleerrordata-args
[`console.group()`]: #consolegrouplabel
[`console.log()`]: #consolelogdata-args
[`console.profile()`]: #consoleprofilelabel
[`console.profileEnd()`]: #consoleprofileendlabel
[`console.time()`]: #consoletimelabel
[`console.timeEnd()`]: #consoletimeendlabel
[`process.stderr`]: process.md#processstderr
[`process.stdout`]: process.md#processstdout
[`util.format()`]: util.md#utilformatformat-args
[`util.inspect()`]: util.md#utilinspectobject-options
[customizing `util.inspect()` colors]: util.md#customizing-utilinspect-colors
[falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy
[inspector]: debugger.md
[note on process I/O]: process.md#a-note-on-process-io
[truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
console Issues and PRs related to the console subsystem. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.