yuheng/doc/fastify-docs/docs/Logging.md
2025-03-19 15:54:28 +08:00

5.1 KiB
Raw Permalink Blame History

Fastify

日志

日志默认关闭,你可以在创建 Fastify 实例时传入 { logger: true } 或者 { logger: { level: 'info' } } 选项来开启它。要注意的是,日志无法在运行时启用。为此,我们使用了 abstract-logging

Fastify 专注于性能,因此使用了 pino 作为日志工具。默认的日志级别为 'info'

开启日志相当简单:

const fastify = require("fastify")({
  logger: true,
});

fastify.get("/", options, function (request, reply) {
  request.log.info("Some info about the current request");
  reply.send({ hello: "world" });
});

在路由函数之外,你可以通过 Fastify 实例上挂载的 Pino 实例来记录日志:

fastify.log.info("Something important happened!");

如果你想为日志配置选项,直接将选项传递给 Fastify 实例就可以了。 你可以在 Pino 的文档中找到全部选项。如果你想指定文件地址,可以:

const fastify = require("fastify")({
  logger: {
    level: "info",
    file: "/path/to/file", // 将调用 pino.destination()
  },
});
fastify.get("/", options, function (request, reply) {
  request.log.info("Some info about the current request");
  reply.send({ hello: "world" });
});

如果需要向 Pino 传送自定义流 (stream),仅需在 logger 对象中添加 stream 一项即可。

const split = require("split2");
const stream = split(JSON.parse);

const fastify = require("fastify")({
  logger: {
    level: "info",
    stream: stream,
  },
});

默认情况下Fastify 给每个请求分配了一个 ID 以便跟踪。如果头部存在 "request-id" 即使用该值,否则会生成一个新的增量 ID。你可以通过 Fastify 工厂函数的 requestIdHeadergenReqId 来进行自定义。

默认的日志工具使用标准的序列化工具,生成包括 reqreserr 属性在内的序列化对象。req 对象是 Fastify Request 对象,而 res 则是 Fastify Reply 对象。可以借由指定自定义的序列化工具来改变这一行为。

const fastify = require("fastify")({
  logger: {
    serializers: {
      req(request) {
        return { url: request.url };
      },
    },
  },
});

响应的 payload 与 header 可以按如下方式记录日志 (即便这是不推荐的做法)

const fastify = require("fastify")({
  logger: {
    prettyPrint: true,
    serializers: {
      res(reply) {
        // 默认
        return {
          statusCode: reply.statusCode,
        };
      },
      req(request) {
        return {
          method: request.method,
          url: request.url,
          path: request.path,
          parameters: request.parameters,
          // 记录 header 可能会触犯隐私法律,例如 GDPR (译注General Data Protection Regulation)。你应该用 "redact" 选项来移除敏感的字段。此外,验证数据也可能在日志中泄露。
          headers: request.headers,
        };
      },
    },
  },
});

:在 req 方法中body 无法被序列化。因为请求是在创建子日志时就序列化了,而此时 body 尚未被解析。

以下是记录 req.body 的一个方法

app.addHook("preHandler", function (req, reply, done) {
  if (req.body) {
    req.log.info({ body: req.body }, "parsed body");
  }
  done();
});

Pino 之外的日志工具会忽略该选项。

你还可以提供自定义的日志实例。将实例传入,取代配置选项即可。提供的示例必须实现 Pino 的接口,换句话说,便是拥有下列方法: infoerrordebugfatalwarntracechild

示例:

const log = require("pino")({ level: "info" });
const fastify = require("fastify")({ logger: log });

log.info("does not have request information");

fastify.get("/", function (request, reply) {
  request.log.info(
    "includes request information, but is the same logger instance as `log`",
  );
  reply.send({ hello: "world" });
});

当前请求的日志实例在生命周期的各部分均可使用。

日志修订

Pino 支持低开销的日志修订,以隐藏特定内容。 举例来说,出于安全方面的考虑,我们也许想在 HTTP header 的日志中隐藏 Authorization 这一个 header

const fastify = Fastify({
  logger: {
    stream: stream,
    redact: ["req.headers.authorization"],
    level: "info",
    serializers: {
      req(request) {
        return {
          method: request.method,
          url: request.url,
          headers: request.headers,
          hostname: request.hostname,
          remoteAddress: request.ip,
          remotePort: request.socket.remotePort,
        };
      },
    },
  },
});

更多信息请看 https://getpino.io/#/docs/redaction