Winston用Mongoose Docslogging

我最近刚刚切换到Winston进行日志logging,并注意到一个问题,当执行后loggingmongoose文档。

例:

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 }); 

所以,基本上我怎样才能得到winston日志logging打印相同的方式,你从console.log? 我猜它必须如何被序列化之前通过调用toJSON()logging。

我每次都必须手动调用.toJSON(),还是让别人做了其他的事情来自动完成这个工作?

警告

我认为winston的预期用途是首先将string消息logging下来(如果需要的话)以及其他元信息。 而且,我不明白为什么你想logging从mongo返回的整个集合,而不是说 – 只是_id s(假设docs可能相当大)。

介绍

我看着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); ... } 

基本上,typesobject单个参数被解释为元。 控制台传输层(默认)主要是在winston / common.js中定义的,下面是meta的处理方式:

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

serialize方法迭代(recursion)在对象的所有键上以形成最终的string(而不是调用.toString或类似的)。

build议的解决scheme

这两个解决scheme都强制winston将单个对象参数解释为不作为元,而是作为消息string。

如果您要支持不同的传输层,则必须修改它们。

更改Winston源代码

只需分叉回购,并对源代码进行相关更改即可。 有很多方法来完成它。 一个丑陋可能是:

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

但是如果对象是mangoose模型,那么在.serialize方法中添加特殊情况会更好,如果这个对象是非常天真和不正确的,

  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 += '}'; } 

(不幸的是,这种方法存在一个问题,因为winston使元的副本和在原型链中被定义得更高的所有方法都丢失了 – 否则就像调用obj.toJSON一样简单,并且肯定会是最优雅和强大的解)

覆盖winston默认行为

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

说明: arguments[0]定义了层次,因此arguments[1]是要logging的实际对象。

我已经在前面的答案中结合了这些想法,提供了一个相当健壮的方法来注销元对象,我已经在生产中运行了好几个月,没有任何问题。

总的想法是覆盖transport.log并将元对象转换为JSONstring,然后再返回。 这确保了元对象被保存为一个对象,因此可以利用诸如prettyPrint等元对象的winston优点。

以下是创build新logging器的代码,带有一个prettyPrint选项:

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

您现在可以像这样使用logging器:

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

这将输出如下所示:

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

简单的解决scheme是将Mongoose模型对象转换为JSONstring,并将其传递给winston函数。 对于数组,您可能必须在循环中调用函数。

 winston.info(JSON.stringify(doc));