JSON formatter doesn't stringify attributes in a consistent order
jorgelf opened this issue · 4 comments
Issue in Linux with Winston 3.2.1 and Node 10.15.1.
Though this isn't a critical error, I find it annoying that the JSON formatter doesn't stringify the resulting object with the attributes always in the same order.
For example, I have a File transport with the errors, splat, timestamp and JSON formatters and the following lines result in different log lines:
logger.info('TEST TEST');
logger.info('TEST %s', 'TEST');
{"message":"TEST TEST","level":"info","timestamp":"2019-04-05T15:09:52.076Z"}
{"level":"info","message":"TEST TEST","timestamp":"2019-04-05T15:09:52.077Z"}
Not sure if that change in behaviour could be considered a bug in Winston itself, but I think these problems could easily be avoided by having the JSON formatter stringify in a consistent order, most likely by using fastSafeStringify.stableStringify instead of the library's default method. Maybe it could be configurable with a new option?
Right now I'm avoiding this annoyance by setting a custom formatter before the JSON one that resets the options' keys in alphabetical order, in case someone has this problem as well:
winston.format(options => {
var keys = Object.keys(options).sort();
for (var i = 0, len = keys.length; i<len; i++) {
var value = options[keys[i]];
delete options[keys[i]];
options[keys[i]] = value;
}
return options;
})()
Totally valid request. If you'd like to add a stable
option to the JSON formatter that uses stableStringify
if true
, we'd be happy to accept a PR. I imagine there's some performance cost to doing that sorting, which is why it's off by default.
This did this trick for me
const formatForFile = winston.format.printf((info) => {
const uid = info.uid ? `,"uid":"${info.uid}"` : ''
const meta = info.meta ? `,"meta":${JSON.stringify(info.meta)}` : ''
return `{"timestamp":"${info.timestamp}","level":"${info.level}","context":"${info.context}","message":"${info.message}"${uid}${meta}}`
})
you can try this one, work for me
format.printf(({ timestamp, level, message, ...leftOver }) =>
JSON.stringify({
timestamp,
level,
message,
...(leftOver || {}),
}),
)