I use Winston for my backend logging I cannot log the object without using JSON.stringify
which is annoying
logger.debug(`Register ${JSON.stringify(
I had to combine the solution provided by @SherloxFR and @Anton.
const Winston = require('winston');
const { format } = Winston;
const options = {
file: {
....
format: format.combine(
format.splat(),
format.json()
),
...
},
console: {
...
format: format.combine(
format.splat(),
format.json()
),
...
}
};
You can see that I added both format.splat()
and format.json()
to the options config in the above code.
const logger = new Winston.createLogger({
transports: [
new Winston.transports.File(options.file),
new Winston.transports.Console(options.console)
],
exitOnError: false // do not exit on handled exceptions
});
That is how I used the options config object. You can actually write the format code inside the transports array but I don't like it that way. It's your choice anyway.
After the configuration like that, the is how I used it in my code
let myObj = {
name: "StackOverflow",
};
logger.info('Content: %o', myObj);
You can also spread it like this if you want
logger.info('Content: %o', {...myObj});
That's all. Winston should log your object with this set up.
You are trying to insert a JSON object directly into the string, so it will print [Object Object]
without the JSON.stringify
.
This is not fixable by configuring Winston, as this problem happens while the string is generated (before the logger.debug
function actually reads it), so a console.log
call would print the same thing.
The first parameter of the logger.*
functions is the message (string), then you can pass a metadata object (JSON).
To use the metadata in your logFormat
function, update your Logger instantiation as follow:
const winston = require('winston')
const { format, transports } = winston
const path = require('path')
const logFormat = format.printf(info => `${info.timestamp} ${info.level} [${info.label}]: ${info.message}`)
const logger = winston.createLogger({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
format: format.combine(
format.label({ label: path.basename(process.mainModule.filename) }),
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
// Format the metadata object
format.metadata({ fillExcept: ['message', 'level', 'timestamp', 'label'] })
),
transports: [
new transports.Console({
format: format.combine(
format.colorize(),
logFormat
)
}),
new transports.File({
filename: 'logs/combined.log',
format: format.combine(
// Render in one line in your log file.
// If you use prettyPrint() here it will be really
// difficult to exploit your logs files afterwards.
format.json()
)
})
],
exitOnError: false
})
Usage:
const req = {
body: {
name: 'Daniel Duuch',
email: 'daniel.duuch@greatmail.com',
password: 'myGreatPassword'
}
}
logger.debug(`Register ${req.body.name} with email ${req.body.email}`, { ...req.body, action: 'register' })
Console output:
2019-05-11 17:05:45 debug [index.js]: Register Daniel Duuch with email daniel.duuch@greatmail.com
Logfile output (prettified by hand, see comment in the transport file format):
{
message: 'Register Daniel Duuch with email daniel.duuch@greatmail.com',
level: 'debug',
timestamp: '2019-05-11 17:05:45',
label: 'index.js',
metadata: {
name: 'Daniel Duuch',
email: 'daniel.duuch@greatmail.com',
password: 'myGreatPassword'
}
}
Hope this solves your issue.
Code for this answer
You can use format.splat()
in your logger config:
const logger = createLogger({
format: combine(
...
format.splat(), // <--
...
),
...
});
...and log object using string interpolation:
let myObj = { /* ... */ };
logger.info('This message will include a complete object: %O', myObj);
Or you just use the
printf
function in conjunction with JSON.stringify
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple(),
winston.format.printf(context => {
const msgstr = JSON.stringify(context.message, null, '\t')
return `[${context.level}]${msgstr}`
}),
),
})
My solution was to use this kind of formatter:
const { format } = winston
const consoleFormat = format.combine(
format.prettyPrint(),
format.splat(),
format.printf((info) => {
if (typeof info.message === 'object') {
info.message = JSON.stringify(info.message, null, 3)
}
return info.message
})
)
now all those options works as expected:
logger.info('plain text')
logger.info('plain text with object %o', { a:1, b: 2} )
logger.info({ a:1, b: 2 })