2018-08-14[JavaScript] winston 的

2018-08-15  本文已影响0人  V_Jan

官方文档:
注意别看错版本,2.x版和3.x版的是有差别的
3.x版: https://github.com/winstonjs/winston
2.x版: https://github.com/winstonjs/winston/tree/2.x
这里我用的是2.4.1版.
1.从最简单的开始,引入winston后使用默认的logger
1.1 利用默认的logger打印出一些log信息

var winston = require('winston');

  winston.log('info', 'Hello distributed log files!');
  winston.info('Hello again distributed logs');

  winston.level = 'debug';
  winston.log('debug', 'Now my debug messages are written to console!');

控制台输出:
注意因为winston写log调用的是回调函数,所以每次运行这段代码log的输出顺序不一定一样。
info: Hello distributed log files!
info: Hello again distributed logs
debug: Now my debug messages are written to console!
当我运行第四次输出的顺序是:
info: Hello distributed log files!
debug: Now my debug messages are written to console!
info: Hello again distributed logs

1.2 修改log的输出目的地
默认情况下,默认的logger只将内容输出到Console。但肯定可以修改的。有两种方式修改log的输出目的地。
1.2.1 你可以通过winston.add(), winston.remove()来添加和移除log传送的途径。 下面代码将详细的log输出到脚本同级目录下的somefile.log里,而且还会带上timestamp**

'use strict';
const winston = require('winston');
/*
 默认情况下,仅在默认记录器上设置控制台传输。您可以通过add()和remove()方法添加或删除传输:
 */
winston.add(winston.transports.File, { filename: 'somefile.log' });
winston.remove(winston.transports.Console); //关闭了控制台输出

/* use default logger
 可以通过winston模块直接访问默认记录器。可以在默认记录器上使用您可以在记录器实例上调用的任何方法:
 */
winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');

winston.level = 'debug';
winston.log('debug', 'Now my debug messages are written to console!');

1.2.2 通过configure() 一次性修改配置, 为什么说一次性呢?因为transports是winston的一个属性,是一组定义log的传出方式,第一种方法的add, remove操作的其实就是这个transports列表
下面这种方式,达到跟1.2.1 的例子一样的效果,而不用winston.remove(winston.transports.Console);

/*
 或者通过一次调用configure()来完成:
 */
'use strict';
const winston = require('winston');

winston.configure({
    transports: [
        new (winston.transports.File)({ filename: 'somefile.log' })
    ]
});

/* use default logger
 可以通过winston模块直接访问默认记录器。可以在默认记录器上使用您可以在记录器实例上调用的任何方法:
 */
winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');

winston.level = 'debug';
winston.log('debug', 'Now my debug messages are written to console!');

log输出到file中是以追加的方式输入,这里我运行了2次就有两次一样的输出

logger输出

2. 接下来说下如何自定义和使用logger, 用default logger总会受限。
2.1 自己实例化并使用logger,一般由3个步骤要走。
a. 用new (wiston.Logger)创建一个logger对象
b. 指定transports列表,说明log要输出到哪里,这里你依旧可以用操作default logger那样用add(), remove()来添加删除transports.
c. 指定输出的log信息的等级,有2种方式, 如下,这两种写法是等同的 .
这里可以看到你可以用哪些log level :
https://github.com/winstonjs/winston/tree/2.x#using-logging-levels

//You can pass a string representing the logging level to the log() method or use the level specified methods defined on every winston Logger.
//logger is an instance of  'new (wiston.Logger)'
logger.error("I am an error");
logger.log('error', "I am an error");

来一段代码:

'use strict';
const winston = require('winston');
const logger = new (winston.Logger)({ //这个圆括号可有可无
    level: 'info',
    transports: [
         //transports用于指定log要输出到哪里,这里输出到Console(大家熟悉的控制台)
         //并且打开了颜色控制开关
        new (winston.transports.Console)({ colorize: true }),//Console可以不要参数:new (winston.transports.Console)(),
        new (winston.transports.File)({ filename: 'somefile.log' }) 
    ]
});
logger.level = 'debug';
logger.info('Hello world');
logger.debug('Debugging info');
logger.error("I am an error"); //这行代码和下面那行的效果一模一样
logger.log('error', "I am an error");

这里你依旧可以用操作default logger那样用add(), remove()来添加删除transports.

  logger
    .add(winston.transports.File)
    .remove(winston.transports.Console);

2.2接下来说说exceptionHandlers
当Exception被抛出时,我们往往希望这个exception可以输出到特定的地方让我们可以troubleshooting.
你可以这么设置, 通过设置.handleExceptions()或者设置exceptionHandlers的属性

winston.handleExceptions(new winston.transports.File({ filename: 'path/to/exceptions.log' }));
 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' })
    ]
  });

今天先到这里,困了。

有些人会遇到下面的问题,那是因为版本错了。比如我这里package.json里指定的是2.4.1版,但是winston.createLogger()是3.x版后才有的。


image.png

当我下载了3.0.0版,这段代码就正常工作了:


image.png

参考:
https://github.com/winstonjs/winston/tree/2.x
https://github.com/winstonjs/winston
http://thisdavej.com/using-winston-a-versatile-logging-library-for-node-js/
https://github.com/winstonjs/winston/issues/1101

image.png

最后附上一份结合KOA框架的logger.js

const path = require('path'),
    winston = require('winston');

module.exports = (app) => {
    let transports = [];
    transports.push(new (winston.transports.Console)({
        colorize: true,
        level: 'info',
        prettyPrint: _jsonPrettyPrint
    }));

    let workerIdSuffix = process.env.workerId ? ("." + process.env.workerId) : "";
    transports.push(new (winston.transports.File)({
        filename: path.join(app.conf.get('log.path'), 'server.log' + workerIdSuffix),
        json: false,
        level: 'info',
        prettyPrint: _jsonPrettyPrint
    }));

    app.logger = new (winston.Logger)({
        transports: transports,
        exceptionHandlers: [
            new (winston.transports.File)({
                filename: path.join(app.conf.get('log.path'), 'error.log' + workerIdSuffix),
                json: false,
                prettyPrint: _jsonPrettyPrint
            }),
            new (winston.transports.Console)({
                colorize: true,
                prettyPrint: _jsonPrettyPrint
            })
        ]
    });

    let accessLogger = new (winston.Logger)({
        transports: [new (winston.transports.File)({
            filename: path.join(app.conf.get('log.path'), 'access.log'),
            json: false,
            level: 'info',
            formatter: (options) => options.message
        })]

    });

    app.accessLogger = {
        log: function () {
            this.request._endTime = this.request._endTime || new Date;

            let request = this.request,
                response = this.response,
                elapsed = request._endTime - request._startTime,
                startTime = moment(request._startTime).format('DD/MMM/YYYY HH:mm:ss ZZ'),
                instanceId = process.env.workerId ? `instance.${process.env.workerId}` : '';

            accessLogger.info(`"${request.ip}" [${startTime}] "${request.method} ${request.url}" "${request.header['user-agent']}" ${response.status} ${elapsed}ms "${instanceId}"`);
        }
    };

    app.use(async (ctx, next) => {
        ctx.request._startTime = new Date();
        await next();
        ctx.request._endTime = new Date();
        app.accessLogger.log.call(ctx);
    });

    app.use(async (ctx, next) => {
        try {
            await next();
        } catch (err) {
            if (err instanceof BadRequestError) {
                _handleBadRequestError(ctx, err)
            } else {
                _handleError(ctx, err);
            }
            ctx.app.emit('error', err, ctx);
        }
    });
};

function _handleBadRequestError(context, err) {
    context.status = 400;
    context.body = {
        error: err.message
    }
}

function _handleError(context, err) {
    context.status = err.status || 500;

    if (err.response && err.response.body && err.response.body.error) {
        context.body = err.response.body;
    } else {
        let cause = err.response && err.response.error ? err.response.error : err;
        context.body = {
            error: cause.message,
            stack: cause.stack ? cause.stack : cause
        };
    }
}

function _jsonPrettyPrint(obj) {
    return JSON.stringify(obj);
}
上一篇下一篇

猜你喜欢

热点阅读