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.

1

7 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*/ ] }); 
3

For 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"} 

See:

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!

4

Another 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.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy