我最近刚刚切换到Winston进行日志记录,并注意到在exec之后记录mongoose文档时出现问题。
示例:
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日志记录以与从console.log获得的方式相同的方式进行打印?我猜测在通过调用toJSON()进行记录之前必须如何序列化。
我是否每次都必须手动调用.toJSON()或让人们做其他事情才能自动完成这项工作?
答案 0 :(得分:4)
我认为winston的用途是首先记录字符串消息,并(如果需要)记录其他元信息。此外,我不明白为什么你想记录从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);
...
}
基本上,类型object
的单个参数被解释为元。
控制台传输层(默认)主要在 winston / common.js 中定义,以下是元的处理方式:
... if (Object.keys(meta).length > 0) {
output += ' ' + (
options.prettyPrint
? ('\n' + util.inspect(meta, false, null, options.colorize))
: exports.serialize(meta)
);
}
serialize 方法迭代(递归)对象的所有键以形成最终字符串(而不是调用.toString
或类似的)。
两种解决方案都强制winston将单个对象参数解释为不是元而是作为消息字符串。
如果您想支持不同的传输层而不是必须修改它们。
只需对repo进行分叉并对源代码进行相关更改。有很多方法可以实现它。一个丑陋的可能是:
meta = args.length === 1 ? {} :
(typeof args[args.length - 1] === 'object' ? args.pop() : {}),
但更好的是在.serialize
方法中添加特殊情况如果对象是mangoose模型,则进行特殊处理,非常幼稚且不正确:
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制作了meta的副本,并且在原型链中定义得更高的所有方法都丢失了 - 否则它就像调用obj.toJSON
一样容易,并且肯定会是最优雅,最强大的解决方案)
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]
是要记录的实际对象。
答案 1 :(得分:3)
我已经结合了之前答案中的想法,为注销元对象提供了一种相当强大的方法,我已经在生产中运行了好几个月没有任何问题。
一般的想法是覆盖transport.log
并将元对象转换为JSON字符串,然后再返回。这可以确保将元对象保留为对象,从而利用winston对元对象的好处,例如prettyPrint
。
以下是使用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]
});
您现在可以像这样使用记录器:
logger.debug('My Mongoose document:', doc);
将输出如下内容:
debug: My Mongoose document: {"_id":"56f0598b130b3cfb16d76b3d","name":"Bob"}
答案 2 :(得分:0)
简单的解决方案是将Mongoose模型对象转换为JSON字符串并将其传递给winston函数。对于Array,您可能需要循环调用函数。
winston.info(JSON.stringify(doc));