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 in console.log() output #29539

Closed
bnb opened this issue Sep 12, 2019 · 19 comments
Closed

util: change in console.log() output #29539

bnb opened this issue Sep 12, 2019 · 19 comments
Labels
console Issues and PRs related to the console subsystem. util Issues and PRs related to the built-in util module.

Comments

@bnb
Copy link
Contributor

bnb commented Sep 12, 2019

  • Version: v10.8.0+
  • Platform: all?
  • Subsystem: util

In v10.8.0, #21624 shipped. This is seemingly a semver major change, despite having shipped in a semver minor release.

What's the change?

The result of:

console.log(1, "hi")

changed from:

1 'hi'

to:

1 hi

Here's an example in the repl:
image

This was surfaced to me by @euantorano from their issue in Nim, in which their tests failed because they were built to expect the old behavior: nim-lang/Nim#12182 (comment)

@bnb bnb changed the title util: regression in console.log() output util: change in console.log() output Sep 12, 2019
@devsnek devsnek added console Issues and PRs related to the console subsystem. util Issues and PRs related to the built-in util module. labels Sep 12, 2019
@devsnek
Copy link
Member

devsnek commented Sep 12, 2019

i don't know if this change is a bug or not, but people really shouldn't ever depend on the output of util.format. its only designed for humans.

@BridgeAR
Copy link
Member

@bnb #21624 is independent from this. It changed in #23162. I am not totally happy with this change as I outlined in the PR. I plan on improving things at some point but it's nothing urgent for me.

I have to agree with @devsnek though that it's not ideal to depend on the output of util.inspect or util.format (which is used in console).

@sam-github
Copy link
Contributor

sam-github commented Sep 12, 2019

If the console.log output changed, I would agree its semver-major, I think its reasonable to depend on the output of console.log(). But, I don't see that happening across the reported v10.8.0+:

Now using node v10.7.0 (npm v6.1.0)
core/node (master $% u=) % node -e 'console.log(1, "hi")'
1 'hi'
core/node (master $% u=) % node -e 'console.log(1, "hi there")'
1 'hi there'
core/node (master $% u=) % nvm i 10.8
v10.8.0 is already installed.
Now using node v10.8.0 (npm v6.2.0)
core/node (master $% u=) % node -e 'console.log(1, "hi")'      
1 'hi'
core/node (master $% u=) % node -e 'console.log(1, "hi there")'
1 'hi there'

Am I missing something, @bnb? Did you mistype the version?

@devsnek
Copy link
Member

devsnek commented Sep 12, 2019

I would only call it semver-major if console.log(string) changed (which hopefully will never change). console is a debug tool, not a serialization tool.

@sam-github
Copy link
Contributor

console is a debug tool,

In the browser, that's true, and some use it like that in node.js, which is very reasonable, but others use it as "node's printf", it even support prinf-like formats.

If I write a reporting tool that does console.log(n, s) in a loop to write some results to stdout, and another tool parses my stdout, that's not crazy.

But like it or not that people should depend on it, the definition of a breaking change is a change that breaks previously running code.There are things that are much more subtle changes in the API, like event ordering, or whether internal, undocumented properties as exposed or not, that we have treated as semver-major, I'd say console.log() output is compatively much more "public" and deserves as much care towards change.

@devsnek
Copy link
Member

devsnek commented Sep 13, 2019

perhaps we need better documentation, but you should be using stuff like process.stdout.write for dependable output. afaik the only format we keep stable is single argument as a string (and the printf formatting that comes out of that first string argument, as required by the console spec)

@sam-github
Copy link
Contributor

Well, that surprises me, and I'm not sure I agree with it. An API with stable output only for certain arg combinations seems a bit cranky, and I'm not seeing any docs distinguishing when a user can and can't depend on the output. All the call patterns seem equally allowed, that some are not equally stable seems worrisome.

Anyhow, at this point, I can't reproduce the bug, and I haven't heard lots of complaints about unstable output, so maybe there is no problem here. However, the "some console.log arg combinations are more equal than others in terms of stability" argument makes me more concerned than comforted.

@bnb
Copy link
Contributor Author

bnb commented Sep 13, 2019

I'm able to reproduce locally on when comparing Node.js v8.x with Node.js v12.x, @devsnek.

Spent some time trying to track down the origin of the actual change since it was obscured by a bunch of layers of indirection. Apologies for the incorrect reference @BridgeAR – did the best I could 😅

On relying on util.format and util.inspect: people are going to use console.log() and they're fundamentally going to expect that the API is one that Just Works™. Convincing people otherwise is a losing battle – ask the folks who build IDEs and DevTools, who've been attempting this for years and never finding purchase. Tossing in a console.log() is – at this point in JavaScript – the go-to debugger for JavaScript developers. We probably shouldn't be breaking such an API.

@addaleax
Copy link
Member

Fwiw, I’m surprised by this change myself – I always thought that when the first argument was not a string, every argument would be util.inspect()’ed by default?

I also don’t see any disadvantage in marking changes like this as semver-major, if we are convinced that they should happen. It might be a bit annoying for backporting, but the ability of our users to rely on stable results should come first, imo.

@silverwind
Copy link
Contributor

I always thought that when the first argument was not a string, every argument would be util.inspect()’ed by default?

That was before my change yes. The string-first case actually ended up in the code othat was doing printf token replacements so that's why it's formatting differed from the others. I don't think that behaviour was ever intended to be that way.

The change appeared in v12.0.0 and is documented on util.format.

v12.0.0 | If the format argument is not a format string, the output string's formatting is no longer dependent on the type of the first argument. This change removes previously present quotes from strings that were being output when the first argument was not a string.

Voting to close this.

@BridgeAR
Copy link
Member

BridgeAR commented Sep 13, 2019

I personally think the old behavior is better than the current one and I would rather revert to that.
IMO it would be ideal to inspect all arguments with util.inspect in case there's more than a single argument (after replacing formatters) instead of checking for the first arguments type. But that would be another breaking change and we would have to check how much impact it would have on the ecosystem.

@sam-github
Copy link
Contributor

If this didn't show up in an 8.x minor, but in 12.0.0 as @silverwind says, then its an allowed breaking change. If we don't like it, then it can be reverted in 13.x if someone is fast. 12.x is stuck with the behaviour, though, for stability.

I like the old behaviour better, I think, but I haven't seen the conversation that led to the change, maybe there was good reason. All of which is tangential to this particular bug report, though.

@bnb
Copy link
Contributor Author

bnb commented Sep 13, 2019

It does indeed seem like it was a breaking change in v12.0.0. Digging through the git logs it seemed like it'd shipped earlier. If it didn't, my bad – this was a major that shipped in a major.

On the topic of reverting, here's one reason why you'd want to revert:

If undefined or null are printed without ANSI colors, it's impossible to tell which is a string and which is a real error.
image

@silverwind
Copy link
Contributor

silverwind commented Sep 17, 2019

it would be ideal to inspect all arguments with util.inspect in case there's more than a single argument (after replacing formatters) instead of checking for the first arguments type

I prefer consistency. Either format all or no arguments, regardless of argument count or type. Format all is probably too much of a breaking change if console starts to log colors everywhere, thought, so I guess it would be a opt-in. Maybe util.format.defaultOptions.formatAll or similar.

@jasnell
Copy link
Member

jasnell commented Sep 17, 2019

+1 to reverting to the original behavior, regardless of the merits of the change, it's a semver-major difference and shouldn't have gone into 12.x

@silverwind
Copy link
Contributor

it's a semver-major difference and shouldn't have gone into 12.x

It was done in 12.0.0, that by definition is a semver-major release. What's the issue?

@sam-github
Copy link
Contributor

The original issue says:

This is seemingly a semver major change, despite having shipped in a semver minor release.

But that is not accurate, the change did not ship, at all, in any 10.x release: I tested 10.7, 10.8 -- the one claimed -- and 10.16.3, none have this change.

@bnb wrote

It does indeed seem like it was a breaking change in v12.0.0.

That comment could be misread as a statement that a major landed inappropriately breaking a release line (though I don't think that was the intention). As @silverwind says, we can land semver-major in the first release on a new release line, but not later, so the behaviour in 12.x isn't a bug.

I can't reproduce anything that shows a break of nodejs' stability commitments.

However, many comments above indicate the change is not desireable.

Should we edit the description so it is accurate?

Or close this and open a new issue requesting the change be reverted in the next semver-major, 13.x?

@Fishrock123
Copy link
Contributor

but you should be using stuff like process.stdout.write for dependable output

This is actually correct, if a bit annoying.

Trott pushed a commit to BridgeAR/node that referenced this issue Oct 14, 2019
This reverts nodejs#23162. The gained
consistency did not outweight the benefit of inspecting the arguments.
Therefore it is best to revert to the former behavior.

Refs: nodejs#29539
Refs: nodejs#23162
@jasnell
Copy link
Member

jasnell commented Jun 19, 2020

I don't think there's anything actionable at this point in time. Closing. Can reopen if necessary

@jasnell jasnell closed this as completed Jun 19, 2020
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. util Issues and PRs related to the built-in util module.
Projects
None yet
Development

No branches or pull requests

8 participants