【问题标题】:How to send logs to different transports with Winston?如何使用 Winston 将日志发送到不同的传输器?
【发布时间】:2016-10-19 08:05:24
【问题描述】:

我在系统中发生了不同的事件,我想将其中一些记录到一个文件中,而将其他事件记录到另一个文件中。

例如:

  • “start”、“stop”等服务器事件将通过名为“ServerLogger”的传输转到“server.log”。
  • 用户事件“login”、“logout”、“register”将与 UsersLogger 一起放入“users.log”中
  • “已付款”、“已拒绝”等付款事件将在 PaymentsLogger 的“payments.log”中。

在系统中我会像这样运行它:

logger.log(ServerLogger, 'start');
logger.log(UsersLogger, 'login','john');
logger.log(PaymentsLogger, 'paid','100', 'john');

我如何让它像这样工作,以便当我想记录到某个特定的记录器时,它会被使用?

我应该像这样将每个记录器注册为新的 winston 实例吗?

const serverLogger = new winston.Logger()
const usersLogger = new winston.Logger()
const paymentsLogger = new winston.Logger()

【问题讨论】:

  • 据我所知,是的,您将需要不同的记录器。或者,一个骇人听闻的解决方案可能是在 winston 上构建自己的记录器,在其中声明所有可能的记录器,并制作一种方便的方法来打印到不同的传输。

标签: node.js logging winston


【解决方案1】:

虽然这可能不是完美的解决方案,但我发现自己处于类似情况。我有不同的文件,它们可能处于活动状态,也可能不处于活动状态,用于管理应用程序的不同部分。

我的解决方案是基于 winston 制作我的“自己的”记录器。由于每个源代码文件都需要一个记录到不同文件的记录器和一个“通用”文件,因此我制作了一个我调用的“生成器”,而不是直接要求 winston:

log.js:

'use strict';

const util = require('util'),
    winston = require('winston'),
    config = require('./config.json');

//If I don't want to log in files and only in console
let testMode = (config.log.mode === 'test');

//"Common" log file
const fileTransport = new (winston.transports.File)({
        timestamp: true,
        name: config.log.all.file,
        filename: config.log.all.file,
        level: config.log.all.level
    }),
//"Common" error log file
    errorTransport = new (winston.transports.File)({
        timestamp: true,
        name: config.log.error.file,
        filename: config.log.error.file,
        level: 'error'
    });

//Logs are also sent in mongoDB, with the same schema as in the files
let mongoTransport = {},
    mongoErrorTransport = {};

if(!testMode) {
    //Add winston.transport.MongoDB
    require('winston-mongodb');

    mongoTransport = new (winston.transports.MongoDB)({
        name: 'all',
        host: config.log.db.host,
        safe: config.log.db.safe,
        collection: 'all',
        level: config.log.all.level,
        db: config.log.db.db
    });
    mongoErrorTransport = new (winston.transports.MongoDB)({
        name: 'error',
        host: config.log.db.host,
        safe: config.log.db.safe,
        collection: 'error',
        level: 'error',
        db: config.log.db.db
    });
}

function getTransports(file) {
    let transports = [];

//Log in the console
    transports.push(new (winston.transports.Console)({
        timestamp: true,
        level: config.log.all.level,
        formatter: (args) => {
            let d = new Date();
            return d.getFullYear() +
                    '/' + d.getMonth(), +
                    '/' + d.getDate(), +
                    ' ' + d.getHours(), +
                    ':' + d.getMinutes(), +
                    ':' + d.getSeconds(), +
                    ':' + d.getMilliseconds(), +
                    ' - ' + file +
                    ' - ' + args.level + 
                    ' -\t' + args.message + 
                    '\t' + util.inspect(args.meta);
        }
    }));

    if(testMode) {
        return transports;
    }

    let name,
        level,
        filename;

    transports.push(fileTransport);
    transports.push(errorTransport);
    transports.push(mongoTransport);
    transports.push(mongoErrorTransport);

//Module specific logs

    if(config.log[file] && config.log[file].file) {
        name = config.log[file].file;
    } else {
        name = file;
    }
    if(config.log[file] && config.log[file].level) {
        level = config.log[file].level;
    } else if(config.log.default && config.log.default.level) {
        level = config.log.default.level;
    } else {
        level = 'info';
    }
    if(config.log[file] && config.log[file].file) {
        filename = config.log[file].file;
    } else if(config.log.default && config.log.default.file) {
        filename = config.log.default.path + file + '.log';
    } else if(config.log.default && config.log.default.path) {
        filename = config.log.default.file;
    } else {
        filename = './log/' + file + '.log';
    }

//Module specific log file

    transports.push(new (winston.transports.File)(
        {
            timestamp: true,
            name: name,
            level: level,
            filename: filename
        }
    ));

//Module specific Mongo collection for logs

    transports.push(new (winston.transports.MongoDB)({
        name: 'mongo' + file,
        host: config.log.db.host,
        safe: config.log.db.safe,
        collection: file,
        level: level,
        db: config.log.db.db
    }));

    return transports;
}

//Generator
module.exports = (file) => {
    let transports = getTransports(file);
    return new (winston.Logger)({
        rewriters: [
            (level, msg, meta) => {
                meta.app = file + '.js';
                return meta;
            }
        ],
        transports: transports
    });
};

被称为:

'use strict';

const Logger = require('./log.js'),
    logger = Logger('myModule');

logger.debug('Hi');
logger.error('Oops');

虽然它远非完美的解决方案,并且可能不适用于您的特定问题,但类似的东西可能比手动创建每个记录器更干净。

【讨论】:

    【解决方案2】:

    提出了一个稍微不同的解决方案,该解决方案适用于相似但不匹配的问题。我想向 cloudwatch 发送两条日志消息,说明作业已开始并已完成,同时在进程运行时进行更多日志记录,以不同方式处理。 (cloudwatch 环境与我们的监控相关,我们有几百个作业通过 cloudwatch 报告失败)。

    所以我只是在将日志记录到 cloudwatch 时添加和删除传输:

    function logSomethingForMonitoring(msg){
      try{
          parentLogger.add(cloudwatch)
          parentLogger.info(msg)
      } finally {
        parentLogger.remove(cloudwatch)
      }
    }
    

    我怀疑这不是超级性能,因此它可能是正常日志的最佳解决方案,但如果这不是正常的日志记录,它似乎可以正常工作。还没有真正测试过,因为就像我说的,我只将它用于两条消息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-21
      • 2015-11-07
      • 1970-01-01
      • 2020-05-18
      • 2018-03-11
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      相关资源
      最近更新 更多