Suppose we used console.log as follows:
console.log('I\'m trying to escape', false);
console.log(false, 'I\'m trying to escape');
This will then output the following:
I'm trying to escape false
false 'I\'m trying to escape'
As can be seen the backslash is treated differently between lines.
Also in the actual shell, the colouring is not present in the first line, but is present in the second line.
(boolean is yellow and string is green)
Is this by design or a bug?
Is this by design or a bug?
I agree that it’s super confusing, but it’s by design. If the first argument is a string, it is used as a format string – the console.log() documentation isn’t great on explaining this, but it links to util.format(). There, we find this sentence:
the first argument is not a string then
util.format()returns a string that is the concatenation of all arguments separated by spaces. Each argument is converted to a string usingutil.inspect().
I really think we should improve the console.log documentation here, though.
I’m reopening this so that we can keep it as a todo for the documentation, hope that’s okay.
I would love to do this one as good first issue, I do have a contribution already but not that relevant, is it okay?
@lovinglyy Yes, go on. :+1:
If you have any questions you can post them here.
I think our behaviour might not be conforming the Console Standard. If I interpret the spec correctly, we should arrive here with logLevel being 'log' and there is no mentioning that the first argument is to be treated differently from the others, or am I missing something?
@silverwind it uses https://console.spec.whatwg.org/#formatter
@devsnek I don't think so. It should branch to Printer on step 5 of Logger when the first argument does not contain a format specifier (e.g. %d):
If first does not contain any format specifiers, perform Printer(logLevel, args).
Should I wait to see if there will be changes in console.log behavior before doing pull requests to the docs?
Working on a fix, not sure if it's feasible but it may just be, so we won't have to document this broken behaviour.
I just checked the spec and to me it leaves a lot of interpretation to the implementer.
The console.log function is an equivalent to Logger('log', data) in the spec. So the Logger itself will check if the first argument contains format specifiers or not. If it does not, it triggers the Printer. The printer definition is very vague and almost everything is open to the implementer in this case.
If the first argument does contain format specifiers, it will call Formatter. The formatter should always return a list which corresponds to an Array in JS. So we actually do not adhere to the spec with util.format() since it returns a string in Node.js. That is somewhat ironic since the spec has an example using Node.js and util.format(). However, we could call this an implementation detail since the formatter function is meant to be a helper function that is not meant to be exposed by the spec.
Looking at the implementations in browsers it seems like Chrome behaves identical to Node.js and Firefox always formats the output.
Both behaviors seem aligned by the spec as the printer function does not specify how the arguments should be visualized:
How the implementation prints args is up to the implementation, but implementations should separate the objects by a space or something similar, as that has become a developer expectation.
Due to this, I would say we can implement this in what ever way we want.
Yes, our code does not strictly implement the abstractions like Formatter from the spec, but that's something that could be solved with a bit of refactoring. The primary issue is thought that util.format is exposed to user code, so we obviously cannot change its return type.
I actually prefer Firefox's formatting to Chrome/Node, but I don't think this is something that can be changed without considerable breakage.
Most helpful comment
Working on a fix, not sure if it's feasible but it may just be, so we won't have to document this broken behaviour.