Winston Logging with Mongoose Docs

Recently, I just switched to Winston for registration and noticed a problem when registering mongoose protocols after exec.

Example:

Model.find().exec(function (err, docs) { console.log(docs) // Prints the collection fine winston.info(docs) // Prints a ton on mongoose stuff, related to the query }); 

So basically, how do I get winston logging for printing the same way you get from console.log? I assume that it must somehow serialize before registering, by calling toJSON ().

Do I have to manually call .toJSON () every time or have people done something else to make this work automatically?

+7
mongoose winston
source share
3 answers

A warning

I think the intended use of winston is to write string messages in first place and (if necessary) additional meta-information. Moreover, I do not quite understand why you want to register the entire collection returned from mongo, and not - let them say it - just _id (assuming docs can be quite large).

Introduction

I looked at the source of winston and here are the relevant parts:

Winston / logger.js

 Logger.prototype.log = function (level) { var self = this, args = Array.prototype.slice.call(arguments, 1); ... var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null, meta = typeof args[args.length - 1] === 'object' ? args.pop() : {}, msg = util.format.apply(null, args); ... } 

Basically, a single argument of type object interpreted as meta. The console transport layer (by default) is mainly defined in winston / common.js, and here is how the meta is processed:

  ... if (Object.keys(meta).length > 0) { output += ' ' + ( options.prettyPrint ? ('\n' + util.inspect(meta, false, null, options.colorize)) : exports.serialize(meta) ); } 

The serialize method iterates (recursively) through all the keys of the object to form the final line (instead of calling .toString or the like).

Suggested Solutions

Both solutions force winston to interpret one object argument not as a meta, but as a message string.

If you want to support different transport levels, they must be changed.

Change winston source code

Just unlock the repo and make the appropriate changes to the source code. There are many ways to achieve it. One ugly one might be:

  meta = args.length === 1 ? {} : (typeof args[args.length - 1] === 'object' ? args.pop() : {}), 

But it would be much better to add a special case in the .serialize method to make a special call if the object is a mangos model, very naive and incorrect:

  else if ('_doc' in obj && 'save' in obj){ var result = []; msg += '{' for(var key in obj['_doc']){ result.push (key + ':' + obj['_doc'][key]); } msg += result.join(', '); msg += '}'; } 

(Unfortunately, there is a problem with this approach, since winston makes a copy of the meta and all methods defined above in the prototype chain are lost - otherwise it would be as simple as calling obj.toJSON , and for sure it would be the most elegant and reliable solution)

Override Winston's Default Behavior

 var original = winston.log; winston.log = function(){ if(arguments.length === 2){ original.call(winston, arguments[0], arguments[1], {}); } else { original.apply(winston, arguments); } } 

Explanation: arguments[0] defines the level, therefore arguments[1] is the actual object to register.

+4
source share

I have combined ideas in previous answers to provide a fairly reliable method for retrieving meta objects that I have been running for many months without any problems.

The general idea is to override transport.log and convert the meta object to a JSON string and vice versa. This ensures that the meta object is saved as an object and, therefore, takes advantage of winston for meta prettyPrint , such as prettyPrint .

Here is the code for creating a new registrar with the prettyPrint option:

 var transport = new (winston.transports.Console)({ prettyPrint: function(meta) { return util.format('%j', meta); } }); var originalLog = transport.log; transport.log = function(level, msg, meta, callback) { if(meta) { if(meta instanceof Error) { // Errors cannot be stringified. meta = meta.toString(); } else { // Keep meta as an object so that winston formats it // nicely, but ensure we don't have any weird ObjectIds meta = JSON.parse(JSON.stringify(meta)); } } return originalLog.call(transport, level, msg, meta, callback); }; var logger = new (winston.Logger)({ transports: [transport] }); 

Now you can use the logger like this:

 logger.debug('My Mongoose document:', doc); 

Which will output something like:

debug: My Mongoose document: {"_id":"56f0598b130b3cfb16d76b3d","name":"Bob"}

+2
source share

A simple solution would be to convert the Mongoose model object to a JSON string and pass this to the winston function. For Array, you may need to call a function in a loop.

 winston.info(JSON.stringify(doc)); 
0
source share

All Articles