I'm using Winston in Node.js for logging. I know I can add metadata individually to each log message but is there a way to specify a default set of metadata that'll be added to every log message (such as the app name) as I don't want to specify it every time I need to send a log message.
17 Answers
For Winston v2 (see comments)
There are now rewriters.
For example, the below will add a property app to every metadata going through this logger.
logger.rewriters.push(function(level, msg, meta) { meta.app = 'myApp'; return meta; }); You can also declare it when building the logger:
new (winston.Logger)({ level: config.log[file].level, rewriters: [ (level, msg, meta) => { meta.app = 'myApp'; return meta; } ], transports: [ /*your transports*/ ] }); 3For Winston v3:
const addAppNameFormat = winston.format(info => { info.appName = "My Program"; return info; }); const logger = winston.createLogger({ format: winston.format.combine( addAppNameFormat(), winston.format.json() ), transports: [new winston.transports.Console()] }); logger.warn('Danger Will Robinson!'); // {"message":"Danger Will Robinson!","level":"warn","appName":"My Program"} There is now (8/20/19) a property defaultMeta that is passed to createLogger that will inject the meta you specify to every log.
const logger = winston.createLogger({ level: 'info', format: winston.format.json(), defaultMeta: { service: 'user-service' }, transports: [ // // - Write to all logs with level `info` and below to `combined.log` // - Write all logs error (and below) to `error.log`. // new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }) ] }); There's no built-in way to do this, but you can definitely add it yourself - here's how:
First, set up your logger like you normally would. For example:
var logger = new (winston.Logger)({ "exitOnError" : true, "transports" : [ new (winston.transports.Console)({ "colorize" : true, "level" : "silly", "silent" : false, "handleExceptions" : false }), ] }); Then override the log() method (this is always called by the level methods - logger.foo() actually calls logger.log('foo').)
logger.log = function(){ var args = arguments; if(args[2]) args[3] = args[2]; args[2] = { "foo" : "bar" } winston.Logger.prototype.log.apply(this,args); } All I'm doing above is making it so when logger.log() is called, it instead calls the above method, which adds the metadata (in this case, an object containing a foo key). Then it calls winston's Logger.log method from the proper context.
The above code would be in a module you create, at the bottom just export the logger:
module.exports = logger;
and import the logger module instead of the winston module in your sub classes.
var logger = require('./logger.js');
Hope that helps!
4Another option with an output more like log4j(s):
There is a documented label property which will add a label to the output (json or line):
var _ = require('lodash'); var winston = require('winston'); var path = require('path'); var baseDir = path.resolve(__dirname, '..'); // SETUP WINSTON LOGGER var container = new winston.Container(); container.add("exception", { console: { handleExceptions: true, timestamp: true, label: "EXCEPTION", colorize: true } }); container.get("exception").exitOnError = false; var keys = []; module.exports = function(filename) { var label = path.relative(baseDir, filename); if (!_.contains(keys, label)) { container.add(label, { console: { handleExceptions: false, level: 'debug', timestamp: true, label: label, colorize: true } }); keys.push(label); } var logger = container.get(label); logger.exitOnError = false; return logger; }; And in the other modules require it like this:
var logger = require('./logger')(__filename); Example output:
2014-07-23T07:05:27.770Z - info: [config/config.js] ....... I was looking for a way to add the github delivery ID to all my logs.
I use winston 3 and defaultMeta getters along with express-http-context
// logger.js const logger = (filename) => winston.createLogger({ ... defaultMeta: { get deliveryId () { return httpContext.get('deliveryId'); }, get deliveryEvent () { return httpContext.get('deliveryEvent'); }, module: filename, }, ... }); module.exports = (fileName) => { return logger(fileName); } in my other modules:
// foo.js const logger = require('./logger')(__filename) module.exports = async(someInput) => { logger.debug('hello ' + someInput) } I've found a better way using util-extend based on what I read on this blog. It will append the data in all cases, which I found useful for catching logger.info vs logger.log("info", message) and won't overwrite your other arguments.
logger.log = function(){ var args = arguments; var level = args[0]; var newArgs = { foo: "bar", baz: "abc" }; var originalMeta = args[2] || {}; args[2] = extend(originalMeta, newArgs); winston.Logger.prototype.log.apply(this,args); }; Will output in the console and in your logs.