【问题标题】:Proper error logging for node applications节点应用程序的正确错误日志记录
【发布时间】:2018-04-27 14:07:18
【问题描述】:

我正在开发一个包含多个模块/服务的快速项目。文件夹结构大致如下所示:

-- app.js
 -- payment_service
    -- routes.js
    -- index.js
    -- models
      -- model_1.js
      -- model_2.js

index.js 中的 API 是唯一公开的 API,它们充当此模块/服务的所有请求的网关。

大多数服务在许多情况下都可能引发操作错误,因此可能需要人工干预来解决问题。所以我需要:

  1. 使用适当的上下文正确记录错误,以便某些人/脚本可以执行必要的操作。
  2. 找出失败的原因。

将有专门的团队负责每项服务。因此,我应该能够区分每个服务的错误日志,以便将其汇总并转发给相关人员。

我决定使用 ELK stash,以便我可以通过脚本生成报告。

我面临的主要问题是我无法保持日志之间的相关性。例如;如果一个请求来了,它通过了五个函数并且每个函数都记录了一些东西,那么我就无法关联这些日志。

一种方法是为每个请求创建一个子记录器并将其传递给所有函数,但这似乎是将记录器实例传递给所有函数的额外开销。

另一种选择是使用verror 之类的东西并仅在服务/模块的入口点进行日志记录,以便整个上下文可以包含在日志中。这种方法在记录错误方面看起来不错,但它对信息和调试日志没有帮助 - 它们在开发和测试阶段帮助了我很多。

为了区分错误日志,我将创建

  1. 每个服务的日志级别错误的专用记录器。
  2. 一个应用范围的通用记录器,用于信息和调试目的。

这是正确的方法吗?

以最简单的方式实现所有要求的最佳方法是什么?

【问题讨论】:

    标签: node.js logging


    【解决方案1】:

    我建议您使用记录器,并且不需要太复杂的东西。例如:

    npm install 12factor-log

    然后在 app.js 附近的根文件夹中创建一个文件(或在 /lib 文件夹中放置库)

    logger.js

    const Log = require('12factor-log');
    
    module.exports = (params) => {
      return new Log(params);
    }
    

    然后在您的模块中,导入您的记录器并在实例化它时传入模块名称,以便您可以跟踪语句的来源...

    model_1.js

    var log = require('./logger')({name: 'model_1'});
    
    // ... 
    
    log.info("Something happened here");
    
    // ...
    
    try {
      // ...
    catch (error) {
      const message = `Error doing x, y, z with value ${val}`;
      log.error(message);
      throw new Error(message);
    }
    

    然后在您的控制器上优雅地处理错误 -> 视图层以获得用户友好的体验。

    您的日志会打印如下内容:

    {"ts":"2018-04-27T16:37:24.914Z","msg":"Something happened here","name":"model_1","type":"info","level":3,"hostname":"localhost","pid":18}
    

    至于日志的相关性,如果您在上面的输出中看到,它包括运行它的机器的主机名,以及模块的名称和严重性级别。您可以将此 JSON 导入 Logstash 并加载到 Elasticsearch 中,它将存储 JSON 以便于搜索和索引。

    见:https://www.elastic.co/guide/en/logstash/current/plugins-filters-json.html

    【讨论】:

    • 您好,当我谈到关联时,我指的是请求中的关联,例如,如果用户请求通过三个函数,我应该能够关联所有三个日志。
    • 您可以使用授权标头和时间戳来解决这个问题。假设每个请求都使用身份验证令牌(即 JWT)进行验证,您可以生成唯一的请求 ID 并将“请求”对象与每个后续调用一起传递。只需在每一个中添加“[request.id] My log statement” ...,您就可以在 Elasticsearch 中轻松搜索该字符串。 (抱歉在我的手机上写这个)
    • 您不需要构建自定义记录器,但您可以构建一个辅助函数来将 ID 和消息连接到日志消息中。示例 logMessage(req, message) 它将连接字符串和请求 ID 并返回您的消息或传入记录器并让它像包装函数一样处理它。
    • 但为此我需要到处传递 req 对象,这是我不想做的。这将是一个开销,我需要在我的服务不提供 http 请求的上下文中模拟它。
    【解决方案2】:

    日志记录很复杂,很多人都在研究它。我建议不要自己这样做。

    所以,我没有遵循自己的建议,而是创建了自己的日志记录包:

    https://www.npmjs.com/package/woveon-logger

    npm install woveon-logger

    这会打印错误和消息的文件和行号,具有日志记录级别和面向方面的日志记录,并且可以在一次调用中转储堆栈跟踪。它甚至有颜色编码选项。如果您遇到困难并需要一些日志记录功能,请告诉我。

    let logger1 = new Logger('log1', {level : 'info', debug : true, showname : true};
    let logger2 = new Logger('log2', {level : 'verbose', debug : true, showname : true};
    ...
    log1.info('Here is a log message, that is on line 23.');
    log1.verbose('Does not show');
    log2.verbose('Shows because log2 is verbose logging');
    log2.setAspect('IO', true);
    log2.aspect('IO', 'Showing aspect IO logging, for logs for IO related operations');
    

    [2018-06-10T10:43:20.692Z] [INFO--] [log1 ] [path/to/myfile:23] 这是第 23 行的日志消息。

    [2018-06-10T10:43:20.792Z] [VERBOS] [log2] [path/to/myfile:25] 显示因为 log2 是详细日志记录

    [2018-06-10T10:43:20.892Z] [IO----] [log2] [path/to/myfile:27] 显示方面 IO 日志记录,用于 IO 相关操作的日志

    此外,还有一些其他功能,例如:

    log1.throwError('Logs this as both a line of logging, and throws the error with the same message');
    log1.printStack('Prints this label next to the stack trace.');
    

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 2013-05-12
      • 1970-01-01
      • 2012-11-25
      • 1970-01-01
      • 2014-10-01
      • 1970-01-01
      相关资源
      最近更新 更多