1. 使用
有两种方式使用winston,一是通过默认的logger,二是实例化自己的Logger,前者设计的目的是在应用程序中共享logger比较方便。
1.1 使用默认Logger
使用默认的logger很方便,直接通过winston模块获取,logger的任何方法都可以通过默认的logger实例得到。
var winston = require('winston'); winston.log('info','Hello distributed log files!'); winston.info('Hello distributed log files!'); winston.level = 'debug'; winston.log('debug','Now my debug message are written to console!');
winston.add(winston.transports.File,{filename:'somefile.log'});
winston.remove(winston.transports.Console);
或者使用configure()来配置。
winston.configure({ transports:[new (winston.transports.File)({filename:'somefile.log'})] });
Winston更多处理方式的文档,可以查看Winston Transports文档。
1.2 自定义Logger
如果你想管理loggers对象的生存期,可以自定义logger对象。
var logger = new (winston.Logger)({ level:'info', transports:[ new (winston.transports.Console)(), new (winston.transports.File)({filename:'somefile.log'}) ] });
它和默认的logger工作方式一样。
logger.log('info','Hello distributed log files!'); logger.info('Hello distributed log files!');
logger.add(winston.transports.File)
.remove(winston.transports.Console);
同样,使用configure方法也可以重新配置winston.Logger。
var logger = new (winston.Logger)({ level:'info', transports:[ new (winston.transports.Console)(), new (winston.transports.File)({filename:'somefile.log'}) ] }); //替换以前整个transports通过一个新的配置 logger.configure({ level:'verbose', transports:[ new (require('winston-daily-rotate-file'))(opts) ] });
2.元数据处理
除了打印字符串信息,winston也能够打印JSON元数据,如下:
winston.log('info','Test Log Message',{anything:'This is metadata'});
不同的日志方式可能导致储存对象会发生变化,有一个比较好的方式去操作元数据:
1.Console:Logger via util.inspect(meta)
2.File:Logger via util.inspect(meta)
3.同种日志类型多种处理方式
通过定义,可以对相同transports的winston.transports.File设置多种不同level处理方式。
var logger = new (winston.Logger)({ transports:[ new (winston.transports.File)({ name : 'info-file', filename:'filelog-info.log', level:'info' }), new (winston.transports.File)({ name:'error-file', filename:'filelog-error.log', level:'error' }) ] });
如果你想删除其中一种处理方式,可以如下:
logger.remove("info-file");
在这个例子中也可以通过删除Transport本身获得相同的效果:
var infoFile = logger.transports[0]; logger.remove(infoFile);
4.分析
除了对消息和元数据进行处理之外,winston对任何logger也有一个简单的分析机制:
//开始分析test, //使用Date.now()来标记一个异步操作 winston.profile("test"); setTimeout(function(){ //结束分析test winston.profile('test'); },1000) //info: test durationMs=1001
所有分析信息默认设置为'info',在这个配置中消息和元数据都是可选的,没有明显的区别,但是我还是说出这个建议或者问题。
5.字符串插值
log提供和util.format一样的字符串插值方法,允许这样打印信息:
logger.log('info','test message %s', 'my string'); //info: test message my string logger.log('info','test message %d',123); //info: test message 123 logger.log('info','test message %j',{number:123},{}); //info: test message {"number":123} logger.log('info','test message %s, %s','first','second',{number:123}); //info: test message first, second number=123 logger.log('info','test message', 'first', 'second', {number:123}); //info: test message first second number=123 logger.log('info','test message %s,%s','first','second',{number:123},function(){}); //info: test message first,second number=123 logger.log('info','test message', 'first','second',{number:123},function(){}); //info: test message first second number=123
6.查询日志
winston提供查询日志接口,和Logger-like类似,具体可以查看Loggly Search API.
var options = { from:new Date - 24 * 60 * 60 * 1000, util:new Date, limit:10, start:0, order:'desc', fields:['./somefile.log'] }; /* 找出昨天到今天的日志 */ winston.query(options,function(err,results){ if(err){ throw err; } console.log(results); });
7.日志流
streaming允许从你选择的处理方式流水返回日志。
winston.stream({start: -1}).on('log',function(log){ console.log(log); });
异常
winston可以在进程中捕获和记录uncaughtException事件。
有两种不同方法能实现这个功能,分别是在默认的logger实例和自定义的实例。
如果你想通过默认的logger来使用这个特征,调用实例的.handleExceptions的方法
//你能增加一个单独的异常日志给.handleException winston.handleException(new winston.transports.File({ filename:'path/to/exception.log' })); //也可以设置.handleException为true当你增加一个处理方式给winston //可以使用.humanReadableUnhandledException选项或者更多可读的异常 winston.add(winston.transports.File,{ filename:'path/to/all-logs.log', handleException:true, humanReadableUnhandledException:true }); //多个处理方式也能被异常操纵 winston.handleException([transports1,transports2,....]);
退出和不退出
默认情况下,winston会退出如果记录到一个未捕获异常之后,如果不想退出。设置exitOnError = false
var logger = new (winston.Logger)({ exitOnError:false }); //或者这样 logger.exitOnError = false;
当自定义logger实例,你可以给一个处理方式传递exceptionHandlers属性或者在任何处理方式设置handlerExceptions。
例子1:
var logger = new (winston.Logger)({ transports:[ new winston.transports.File({ filename:'path/to/all-logs.log' }) ], exceptionHandlers:[ new winston.transports.File({ filename:'path/to/exceptions.log' }) ] });
例子2:
var logger = new (winston.Logger)({ transports:[ new winston.transports.Console({ json:true, handleException:true }) ], exitOnError:false });
exitOnError选项也可以是一个函数,能防止某些错误的类型二退出。
function ignoreEpipe(err){ return err.code !== 'EPIPE'; } var logger = new (winston.Logger)({ exitOnError:ignoreEpipe }); //或者像这样: logger.exitOnError = ignoreEpipe;
日志等级
这个很简单了
进一步阅读
winston的事件和回调
每一个winston.Logger实例也都是一个EventEmitter实例,一个log事件被触发当写日志成功。
logger.on('logging',function(transport,level,msg,meta){ //msg和meta已经用level记录在[transport] }); logger.info('CHILL WINSTON',{seriously:true});
值得一提的是,logger还能触发一个“错误”事件,如果你不想操作异常,你可以用来处理或者忽略它
logger.on("error",function(err){ /* do something */ }); //或者忽略它 logger.emitErrs = false;
每个logging方法在最后一个字段也有一个可选的回调函数,它触发当所有的transports已经特殊信息完毕
logger.info('CHILL WINSTON',{seriously:true},function(err.level.msg,meta)){ //msg和meta已经用level记录在[transport] }
多个winston类目的日志
常常在一个大的,比较复杂的应用中是非常必要使用多个日志实例。每个日志对应不同的特性或者类型,winston有两种方式来处理:
通过winston.loggers或者winston.Container的实例。事实上,winston.loggers也是winston.Container预先定义的实例。
winston.loggers.add('category1',{ console:{ level:'silly', colorize:true, label:'category one' }, file:{ filename:'/path/to/some/file' } }); winston.loggers.add('category2',{ couchdb:{ host:'127.0.0.1', port:5984 } });
注意:在应用中,你要访问你预配置的logger,你需要require("winston")在你的文件中。
var winston = require("winston"); var category1 = winston.loggers.get('category1'); category1.info('logging from you Ioc Contains-based logger');
如果你喜欢Container你可以实例化
var winston = require("winston"), container = new winston.container(); container.add('category1',{ console:{ level:"silly", colorize:true }, file:{ filename:'path/to/some/file' } })
过滤和重写
过滤允许修正日志消息的内容,重写允许修改日志元数据,即掩盖日志中不出现的数据。
过滤和重写都是一个函数数组,它被创建一个winston.Logger对象时被提供。
var logger = new winston.Logger({ rewriters:[function(level,msg,meta){}], filters:[function(level,msg,meta){}] });
就像其他数组一样,修改日志能运行在winston内部,也没有什么副作用。
var logger = new winston.Logger({ rewriters:[function(level,msg,meta){}], filters:[function(level,msg,meta){}] }); logger.filters.push(function(level,msg,meta){ return meta.production ? maskCardNumbers(msg) :msg; }); logger.info('transaction with card number 1234567890123456 successful');
这个输出的结果可能是:
info:transaction with card number 123456****2345 successful
而重写,你想把creditCard元数据重新实例化:
logger.rewriters.push(function(level,msg,meta){ if(meta.creditCard){ meta.creditCard = maskCardNumbers(meta.creditCard); } return meta; }); logger.info('transaction ok',{creditCard:123456789012345});
输出结果可能是:
info:transaction ok creditCard=123456****2345
增加自定义日志处理方式
增加一个自定义处理方式非常简单,所有需要做的是接受一些选项,设置名字,执行log()方法,和winston暴露的一些transports(处理方式)
var util = require('util'), winston = require('winston'); var CustomLogger = winston.transports.CustomLogger = function(options){ //logger的名字 this.name = 'customLogger'; //选项设置一个等级 this.level = options.level || "info"; //配置你想要的处理方式 } // 继承winston.Transports,你能利用基本函数和.handlerExceptions() util.inherit(CustomLogger,winston.Transports); CustomLogger.prototype.log = function(level,msg,meta,callback){ callback(null,true); }
自定义日志格式
指定自定义格式应该为transport设置格式化函数,目前支持格式化的transports是:Console, File和Memory。可选对象作为参数传递给格式化对象。
一般属性有:时间戳、等级、消息、和元数据,取决于transports类型可能会有额外的属性。
var logger = new (winston.Logger)({ transports:[ new (winston.transports.Console)({ timestamp:function(){ return Date.now(); } formatter:function(options){ //返回字符串给logger return options.timestamp() + ' ' +options.level.toUpperCase() + '' (options.message ? options.message: "") + (options.meta && Object.keys(options.meta).length ? ' ' + JSON.string(options.meta):""); } }); ] }); logger.info('Data to log');