在几个模块中使用winston

时间:2013-01-25 22:17:21

标签: node.js winston

我有几个模块 - 比方说server.js,module1.js,...,moduleN.js。

我想在server.js中定义日志文件:

winston.add(winston.transports.File, { filename: 'mylogfile.log' });

然后在我的所有模块中使用它。

最好的方法是什么?我可以在每个模块中exports.winston=winston;然后在server.js中设置它,但有没有更好的解决方案?

提前谢谢!

11 个答案:

答案 0 :(得分:92)

默认的记录器概念处理得很好。

Winston定义了一个默认记录器,任何直接需要(以及后续要求)的winston都将检索。因此,您只需配置一次此默认记录器,并且可以通过vanilla require('winston')在其光荣的调整多传输模式下使用后续模块。

e.g。这是我完整的日志记录设置,定义了3个传输。我有时会将Loggly换成MongoDB。

server.js

var logger=require('./log.js'); 
// requires winston and configures transports for winstons default logger- see code below.

所有其他.js文件

var logger=require('winston'); // this retrieves default logger which was configured in log.js
logger.info("the default logger with my tricked out transports is rockin this module");

log.js - 这是DEFAULT记录器的一次性配置

var logger = require('winston');
var Loggly = require('winston-loggly').Loggly;
var loggly_options={ subdomain: "mysubdomain", inputToken: "efake000-000d-000e-a000-xfakee000a00" }
logger.add(Loggly, loggly_options);
logger.add(winston.transports.File, { filename: "../logs/production.log" });
logger.info('Chill Winston, the logs are being captured 3 ways- console, file, and Loggly');
module.exports=logger;

或者对于更复杂的场景,您可以使用winston容器并从其他模块中的命名容器中检索记录器。我没有用过这个。

我唯一的问题是我的部署主机上缺少日志目录,很容易修复。

希望这有帮助。

答案 1 :(得分:12)

我所做的(可能不是最好的方式)是使用“全局”模块,我通过我的应用程序导出我使用的所有内容。 例如:

//Define your winston instance
winston.add(winston.transports.File, { filename: 'mylogfile.log' });
exports.logger = winston;

exports.otherGlobals = ....

现在只需要从其他模块中使用这个全局使用的模块

var Global = require(/path/to/global.js);

因为文件在第一次加载后被缓存(您可以通过在全局中包含日志语句来验证它;它只会记录一次),再次包含它的开销很小。将所有内容放在一个文件中也比在每个页面上使用所有全局使用的模块更容易。

答案 2 :(得分:9)

我想使用自定义颜色和关卡。

所以我删除了默认的console-transport并设置了一个彩色的

这是我的 logger.js

var logger = require('winston');

logger.setLevels({
    debug:0,
    info: 1,
    silly:2,
    warn: 3,
    error:4,
});
logger.addColors({
    debug: 'green',
    info:  'cyan',
    silly: 'magenta',
    warn:  'yellow',
    error: 'red'
});

logger.remove(logger.transports.Console);
logger.add(logger.transports.Console, { level: 'debug', colorize:true });

module.exports = logger;



从app.js加载

var logger = require('./lib/log.js');  



从其他模块加载

 var logger = require('winston');        

答案 3 :(得分:6)

稍微偏离主题(因为OP询问温斯顿),但我喜欢'儿童记录器'班扬的做法:

var bunyan = require('bunyan');
var log = bunyan.createLogger({name: 'myapp'});

app.use(function(req, res, next) {
  req.log = log.child({reqId: uuid()});
  next();
});

app.get('/', function(req, res) {
  req.log.info({user: ...});
});

它解决了OP的问题,因为记录器可通过req对象获得(因此不需要在每个模块中使用(log)')。此外,属于特定请求的所有日志条目都将具有将它们连接在一起的唯一ID。

{"name":"myapp","hostname":"pwony-2","pid":14837,"level":30,"reqId":"XXXX-XX-XXXX","user":"...@gmail.com","time":"2014-05-26T18:27:43.530Z","v":0}

我不确定温斯顿是否支持这一点。

答案 4 :(得分:4)

我正在创建一个新的Winston记录器。

<强> log.js

'use strict';

const winston = require('winston');

module.exports = new(winston.Logger)({
    transports: [
        new(winston.transports.Console)({
            level: 'info'
        })
    ]
});

<强> a.js

const log = require('./log');

log.info("from a.js");

<强> b.js

const log = require('./log');

log.info("from b.js");

答案 5 :(得分:2)

我正在使用 Winston 3.0.0 。 而且,似乎配置默认记录器的方式已经改变了一点。 对我有用的方法是:

log.js //全局记录器的设置

const winston= require('winston');

winston.configure({
  level:"debug",
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.simple()
  ),
  transports: [
    new winston.transports.Console()
  ]
});

其他部分相同。 在您的应用程序开头,require('log.js'),还有require ('winston'), 在所有其他文件中,只需require('winston')

答案 6 :(得分:2)

这是我的Winston记录器配置,版本为3.2.1

它将日志存储在application.log文件中,并且为了进行错误堆栈跟踪,我正在使用errors({ stack: true })printf函数中的一个小技巧来在错误情况下打印堆栈跟踪。

配置

const {format, transports} = require('winston');
const { timestamp, colorize, printf, errors } = format;
const { Console, File } = transports;
LoggerConfig = {
        level: process.env.LOGGER_LEVEL || 'debug',
        transports: [
            new Console(),
            new File({filename: 'application.log'})
        ],
        format: format.combine(
            errors({ stack: true }),
            timestamp(),
            colorize(),
            printf(({ level, message, timestamp, stack }) => {
                if (stack) {
                    // print log trace 
                    return `${timestamp} ${level}: ${message} - ${stack}`;
                }
                return `${timestamp} ${level}: ${message}`;
            }),
        ),
        expressFormat: true, // Use the default Express/morgan request formatting
        colorize: false, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
        ignoreRoute: function (req, res) {
            return false;
        } // optional: allows to skip some log messages based on request and/or response
}

声明

我在express-winston中使用了相同的配置,并且也用于常规日志。我全局声明了__logger对象,因此您不必每次都在每个文件中导入。通常,在节点js中,所有带有2次下划线(__)的全局变量前缀,因此遵循此方法将是一个不错的选择。

Server.js

const winston = require('winston');
const expressWinston = require('express-winston');

/**
 * winston.Logger
 * logger for specified log message like console.log
 */
global.__logger = winston.createLogger(LoggerConfig);
/**
 * logger for every HTTP request comes to app
 */
app.use(expressWinston.logger(LoggerConfig));

使用

__logger是全局的,因此您可以在任何地方使用它,例如:

blog.controller.js

function save(req, res) {
  try {
    __logger.debug('Blog add operation');
    .
    .
    return res.send(blog);
  } catch (error) {
    __logger.error(error);
    return res.status(500).send(error);
  }
}

希望这会有所帮助!

答案 7 :(得分:2)

我使用工厂函数并传入模块名称,以便可以将其添加到元数据:

logger-factory.js

const path = require('path');
const { createLogger, format, transports } = require('winston');
const { combine, errors, timestamp } = format;

const baseFormat = combine(
  timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
  errors({ stack: true }),
  format((info) => {
    info.level = info.level.toUpperCase();
    return info;
  })(),
);

const splunkFormat = combine(
  baseFormat,
  format.json(),
);

const prettyFormat = combine(
  baseFormat,
  format.prettyPrint(),
);

const createCustomLogger = (moduleName) => createLogger({
  level: process.env.LOG_LEVEL,
  format: process.env.PRETTY_LOGS ? prettyFormat : splunkFormat,
  defaultMeta: { module: path.basename(moduleName) },
  transports: [
    new transports.Console(),
  ],
});

module.exports = createCustomLogger;

app-harness.js(这样我就可以运行导出的索引模块)

const index = require('./index');

// https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html
const sampleEvent = {
  "Records": [
    {
      "eventVersion": "2.1",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-2",
      "eventTime": "2019-09-03T19:37:27.192Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "AWS:AIDAINPONIXQXHT3IKHL2"
      },
      "requestParameters": {
        "sourceIPAddress": "205.255.255.255"
      },
      "responseElements": {
        "x-amz-request-id": "D82B88E5F771F645",
        "x-amz-id-2": "vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo="
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "828aa6fc-f7b5-4305-8584-487c791949c1",
        "bucket": {
          "name": "lambda-artifacts-deafc19498e3f2df",
          "ownerIdentity": {
            "principalId": "A3I5XTEXAMAI3E"
          },
          "arn": "arn:aws:s3:::lambda-artifacts-deafc19498e3f2df"
        },
        "object": {
          "key": "b21b84d653bb07b05b1e6b33684dc11b",
          "size": 1305107,
          "eTag": "b21b84d653bb07b05b1e6b33684dc11b",
          "sequencer": "0C0F6F405D6ED209E1"
        }
      }
    }
  ]
};

index.handler(sampleEvent)
  .then(() => console.log('SUCCESS'))
  .catch((_) => console.log('FAILURE'));

index.js

const logger = require('./logger-factory')(__filename);
const app = require('./app');

exports.handler = async function (event) {
  try {
    logger.debug('lambda triggered with event', { event });
    await app.run(event);
    logger.debug(`lambda finished`);
  } catch(error) {
    logger.error('lambda failed: ', error);
    // rethrow the error up to AWS
    throw error;
  }
}

app.js

const logger = require('./logger-factory')(__filename);

const run = async (event) => {
  logger.info('processing S3 event', event);
  try {
    logger.info('reading s3 file')
    // throws because I used "Record" instead of "Records"
    const s3 = event.Record[0].s3;
    // use s3 to read the file
  } catch (error) {
    logger.error('failed to read from S3: ', error);
    throw error;
  }
};

module.exports = { run };

当我在WARN级本地运行应用程序时:

~/repos/ghe/lambda-logging (master * u=)> LOG_LEVEL=warn node -r dotenv/config ./src/app-harness.js
{
  module: 'app.js',
  level: 'ERROR',
  message: "failed to read from S3: Cannot read property '0' of undefined",
  stack: "TypeError: Cannot read property '0' of undefined\n" +
    '    at Object.run (/Users/jason.berk/repos/ghe/lambda-logging/src/app.js:8:28)\n' +
    '    at Object.exports.handler (/Users/jason.berk/repos/ghe/lambda-logging/src/index.js:7:15)\n' +
    '    at Object.<anonymous> (/Users/jason.berk/repos/ghe/lambda-logging/src/test-harness.js:44:7)\n' +
    '    at Module._compile (internal/modules/cjs/loader.js:1158:30)\n' +
    '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\n' +
    '    at Module.load (internal/modules/cjs/loader.js:1002:32)\n' +
    '    at Function.Module._load (internal/modules/cjs/loader.js:901:14)\n' +
    '    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)\n' +
    '    at internal/main/run_main_module.js:18:47',
  timestamp: '2020-05-11 17:34:06'
}
{
  module: 'index.js',
  level: 'ERROR',
  message: "lambda failed: Cannot read property '0' of undefined",
  stack: "TypeError: Cannot read property '0' of undefined\n" +
    '    at Object.run (/Users/jason.berk/repos/ghe/lambda-logging/src/app.js:8:28)\n' +
    '    at Object.exports.handler (/Users/jason.berk/repos/ghe/lambda-logging/src/index.js:7:15)\n' +
    '    at Object.<anonymous> (/Users/jason.berk/repos/ghe/lambda-logging/src/test-harness.js:44:7)\n' +
    '    at Module._compile (internal/modules/cjs/loader.js:1158:30)\n' +
    '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\n' +
    '    at Module.load (internal/modules/cjs/loader.js:1002:32)\n' +
    '    at Function.Module._load (internal/modules/cjs/loader.js:901:14)\n' +
    '    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)\n' +
    '    at internal/main/run_main_module.js:18:47',
  timestamp: '2020-05-11 17:34:06'
}

当我以DEBUG级跑步时:

~/repos/ghe/lambda-logging (master * u=)> LOG_LEVEL=debug node -r dotenv/config ./src/test-harness.js 
{
  module: 'index.js',
  event: {
    Records: [
      {
        eventVersion: '2.1',
        eventSource: 'aws:s3',
        awsRegion: 'us-east-2',
        eventTime: '2019-09-03T19:37:27.192Z',
        eventName: 'ObjectCreated:Put',
        userIdentity: { principalId: 'AWS:AIDAINPONIXQXHT3IKHL2' },
        requestParameters: { sourceIPAddress: '205.255.255.255' },
        responseElements: {
          'x-amz-request-id': 'D82B88E5F771F645',
          'x-amz-id-2': 'vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo='
        },
        s3: {
          s3SchemaVersion: '1.0',
          configurationId: '828aa6fc-f7b5-4305-8584-487c791949c1',
          bucket: {
            name: 'lambda-artifacts-deafc19498e3f2df',
            ownerIdentity: { principalId: 'A3I5XTEXAMAI3E' },
            arn: 'arn:aws:s3:::lambda-artifacts-deafc19498e3f2df'
          },
          object: {
            key: 'b21b84d653bb07b05b1e6b33684dc11b',
            size: 1305107,
            eTag: 'b21b84d653bb07b05b1e6b33684dc11b',
            sequencer: '0C0F6F405D6ED209E1'
          }
        }
      }
    ]
  },
  level: 'DEBUG',
  message: 'lambda triggered with event',
  timestamp: '2020-05-11 17:38:21'
}
{
  module: 'app.js',
  Records: [
    {
      eventVersion: '2.1',
      eventSource: 'aws:s3',
      awsRegion: 'us-east-2',
      eventTime: '2019-09-03T19:37:27.192Z',
      eventName: 'ObjectCreated:Put',
      userIdentity: { principalId: 'AWS:AIDAINPONIXQXHT3IKHL2' },
      requestParameters: { sourceIPAddress: '205.255.255.255' },
      responseElements: {
        'x-amz-request-id': 'D82B88E5F771F645',
        'x-amz-id-2': 'vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo='
      },
      s3: {
        s3SchemaVersion: '1.0',
        configurationId: '828aa6fc-f7b5-4305-8584-487c791949c1',
        bucket: {
          name: 'lambda-artifacts-deafc19498e3f2df',
          ownerIdentity: { principalId: 'A3I5XTEXAMAI3E' },
          arn: 'arn:aws:s3:::lambda-artifacts-deafc19498e3f2df'
        },
        object: {
          key: 'b21b84d653bb07b05b1e6b33684dc11b',
          size: 1305107,
          eTag: 'b21b84d653bb07b05b1e6b33684dc11b',
          sequencer: '0C0F6F405D6ED209E1'
        }
      }
    }
  ],
  level: 'INFO',
  message: 'processing S3 event',
  timestamp: '2020-05-11 17:38:21'
}
{
  message: 'reading s3 file',
  level: 'INFO',
  module: 'app.js',
  timestamp: '2020-05-11 17:38:21'
}
{
  module: 'app.js',
  level: 'ERROR',
  message: "failed to read from S3: Cannot read property '0' of undefined",
  stack: "TypeError: Cannot read property '0' of undefined\n" +
    '    at Object.run (/Users/jason.berk/repos/ghe/lambda-logging/src/app.js:8:28)\n' +
    '    at Object.exports.handler (/Users/jason.berk/repos/ghe/lambda-logging/src/index.js:7:15)\n' +
    '    at Object.<anonymous> (/Users/jason.berk/repos/ghe/lambda-logging/src/test-harness.js:44:7)\n' +
    '    at Module._compile (internal/modules/cjs/loader.js:1158:30)\n' +
    '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\n' +
    '    at Module.load (internal/modules/cjs/loader.js:1002:32)\n' +
    '    at Function.Module._load (internal/modules/cjs/loader.js:901:14)\n' +
    '    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)\n' +
    '    at internal/main/run_main_module.js:18:47',
  timestamp: '2020-05-11 17:38:21'
}
{
  module: 'index.js',
  level: 'ERROR',
  message: "lambda failed: Cannot read property '0' of undefined",
  stack: "TypeError: Cannot read property '0' of undefined\n" +
    '    at Object.run (/Users/jason.berk/repos/ghe/lambda-logging/src/app.js:8:28)\n' +
    '    at Object.exports.handler (/Users/jason.berk/repos/ghe/lambda-logging/src/index.js:7:15)\n' +
    '    at Object.<anonymous> (/Users/jason.berk/repos/ghe/lambda-logging/src/test-harness.js:44:7)\n' +
    '    at Module._compile (internal/modules/cjs/loader.js:1158:30)\n' +
    '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\n' +
    '    at Module.load (internal/modules/cjs/loader.js:1002:32)\n' +
    '    at Function.Module._load (internal/modules/cjs/loader.js:901:14)\n' +
    '    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)\n' +
    '    at internal/main/run_main_module.js:18:47',
  timestamp: '2020-05-11 17:38:21'
}

答案 8 :(得分:0)

如果要使记录器成为全局变量,则必须像这样将其分配给全局变量来专门进行

logger.js

var winston = require('winston')

var winston = winston.createLogger({
transports: [
  new (winston.transports.Console)(),
  new (winston.transports.File)({
    filename: './logs/logger.log'
  })
]
});
module.exports=winston;

app.js

let logger  = require('./logger')
global.__logger = logger

someController.js

__logger.info('created log successfully')

注意:最好为每个全局变量分配一个前缀,这样您就知道这是一个全局变量。我使用__作为前缀(双下划线)

答案 9 :(得分:0)

在我们的团队中,我们创建了一个私有的npm软件包,其中包含所有默认配置(如您在前面的答案中所示)

我只有一个问题:将logger对象声明为全局对象以避免在每个模块中导入都是一个好习惯吗?

答案 10 :(得分:0)

只需创建logger.js并放入

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.simple()
  ),
  transports: [
    new winston.transports.Console()
  ]
});

module.exports = logger

然后,您可以在任何地方使用它,因为logger现在是单例。

const logger = require('./utils/logger');
logger.info('Hello!');

这甚至为您提供了必要时交换日志记录库的选项。公认的答案是完全错误的,离意大利面条式编码仅一步之遥。