【问题标题】:Would logging request individually good practice or clutter?日志记录会单独要求良好的做法还是混乱?
【发布时间】:2017-02-12 21:29:40
【问题描述】:

我正在开发一个 API,它会进行大量调用,其中一些需要出于各种原因彻底记录。

目前,我记录了函数中发生的所有输入/输出/处理,并且 API 可以完美运行,因此似乎不需要增加记录量。

但我想到的一个想法是为每个传入的 API 调用分配一个 UUID,这将遵循内部函数中的日志记录。

虽然它会在每个函数中创建大量附加参数来遵循 UUID,但我想知道这是否是常见做法,是否应该在需要之前实现它,而要做的更改量是易于管理。

例如:

显然真正的代码要复杂得多,并且不使用console.log 进行日志记录

const express = require('express'),
    fs = require('fs'),
    config = require('./config.json'),
    app = express();

function foo(bar, callback) {
    console.log(bar);
    fs.open(bar, (err, data) => {
        if(err) {
            console.err(err);
            callback(err);
        } else {
            console.log(data)
            callback(null, data);
        }
    });
}

app.get('/foo', (req, res) => {
    console.log(req.body);
    foo(req.body.bar, (err, result) => {
        if(err) {
            console.err(err);
            res.send(err);
        } else {
            console.log(result)
            res.send(null, result);
        }
    });
});

app.listen(config.port);

收件人:

const express = require('express'),
    UUID = require('uuid-generator'),
    fs = require('fs'),
    config = require('./config.json'),
    app = express();

function foo(uuid, bar, callback) {
    console.log(uuid + ': ' + bar);
    fs.open(bar, (err, data) => {
        if(err) {
            console.err(uuid + ': ' + err);
            callback(err);
        } else {
            console.log(uuid + ': ' + data)
            callback(null, data);
        }
    });
}

app.use((req, res, next) => {
    req.id = new UUID();
    next();
});

app.get('/foo', (req, res) => {
    console.log(req.id + ': ' + req.body);
    foo(req.id, req.body.bar, (err, result) => {
        if(err) {
            console.err(req.id + ': ' + err);
            res.send(err);
        } else {
            console.log(req.id + ': ' + result)
            res.send(null, result);
        }
    });
});

app.listen(config.port);

如果函数失败或系统崩溃,我们可以准确识别导致错误的调用,以及更容易遵循的路径。

缺点是,它需要大量的工作来实现,并且可能没有那么有用,因为我们已经可以从不同的输入/输出和其他已经实现的日志中推断出所遵循的路径。

TL:DR:以这种粒度级别记录每个请求是常见的做法,还是保留用于特定用例?

是否有任何工具可以自动完成并且我不应该在代码中关心它?

【问题讨论】:

    标签: javascript node.js logging


    【解决方案1】:

    我无法真正评论这是否是普遍的做法,但在我最近开发的产品中,这是相当普遍的做法。

    根据用例,我可以看到通过系统跟踪单个请求的很多价值。高流量 API 往往会从中受益,由微服务组成的系统也是如此,您可能需要通过多个连接服务来跟踪请求。

    例如,如果您有一个 API 接受需要存储的输入,那么能够将特定输入数据与失败的数据库写入相关联会非常有用。

    如果您决定要在请求中添加 UUID,那么 express-request-id 中间件是一个有用的模块,它会为您生成 UUID 并将其添加到响应标头中。如果传入请求标头中存在 UUID,它也可以采用 UUID 并使用它,这对于跟踪服务之间的请求很有用。

    我还没有看到任何节点可以完全抽象记录单个请求,但我没有寻找过。

    【讨论】:

      【解决方案2】:

      记录请求而不是 stdout/stderr 或文件是一种很好的做法。最好的方法是通过 TCP 套接字将它发送到类似 Logz.io 或自己的 Logstash 实例(可以安装在同一台或不同的机器上)。 Logstash 应该连接到托管的 AWS Elasticsearch 服务或安装在本地或远程机器上的服务。 如果日志量不大,可以使用 docker-compose 快速组装(为了高吞吐量,我建议使用专用的资源丰富的服务器)

      ELK Stack 是一个很棒的工具集,用于监控、日志聚合、高级查询,以便通过成千上万的日志条目查找特定请求和有关它的详细信息。它是面向搜索的nosql数据库,几乎已经成为业界最先进的日志聚合工具之一。

      Logstash 通过不同的协议接收日志,将它们发送到 NoSQL Elasticsearch,然后您可以使用 Kibana 前端浏览、构建仪表板和聚合您的日志数据。

      按照已经建议的方式添加 X-Request-Id 标头以及其他基础设施元数据到您的日志和时间指标是有原因的。

      这是一个允许您在本地机器上设置整个 ELK 工具集的项目 https://github.com/deviantony/docker-elk

      日志记录可以通过以下方式完成: -https://www.npmjs.com/package/winston(使用https://www.npmjs.com/package/winston-logstash) - https://www.npmjs.com/package/bunyan(与https://www.npmjs.com/package/bunyan-logstash

      这两个库都有大量用于其他类型日志记录的插件

      【讨论】:

      • 我想跟踪服务器内部的请求,应答它的调用。我不需要在不同的服务器之间跟踪它。此外,日志已经发送到服务器,我使用console.log 不显示连接和设置日志格式的代码。
      • 你可以试试这个:node.js的多传输异步日志库github.com/winstonjs/winston
      • 你可以使用某种模块来记录日志——比如winston——他们有很多插件支持非常不同的传输并且专门用于那个puprose——配置日志记录,记录uncaughtExceptions,创建正确的访问日志(您想要的)。 ELK 可以是本地的 - 只需根据您在 OP 中的要求 - 这是一个完美的匹配。本地 ELK 可以以非常简单的方式安装 - 使用 docker-compose 。
      • @Caspain ha 我们在同一时间发了帖子
      • 在我的回答中添加了对您非常有用的链接
      【解决方案3】:
      1. 你看过morgan。 将每个请求记录在文件、数据库等中是快速中间件。 也许你甚至可以将日志扔到logstash。

      2. 现在,与其将 UUID 作为单独的参数传递给每个函数,不如将其传递给现有的数据 json,以传递给每个函数。

      我认为您应该更改代码以在每条日志消息中包含 uuid,因为这将非常有助于您专注于基于 uuid 的大型日志文件中特定请求的跟踪。

      希望对你有帮助。

      【讨论】:

      • 问题是我不会将完整的请求传递给每个函数,因为这会破坏可读性,我想要的是一种干净的方式来从每个函数访问该 id 而无需显式传递它并且弄乱我的代码。摩根看起来不错,但并没有解决整个问题。
      • @DrakaSAN 嗨,我不是在谈论将 req 对象传递给每个要调用的函数,而是将 uuid 作为当前 json 中的额外值传递给函数作为参数。
      • @DrakaSAN 如果要在 java 中实现相同的目标,我们使用执行当前请求的线程的本地缓存。我们在我目前正在开发的节点应用程序中实现了相同的目标。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-05
      • 2014-12-25
      • 2010-11-14
      • 2021-11-10
      • 1970-01-01
      相关资源
      最近更新 更多