Skip to Content

日志记录

启用日志记录

默认情况下,日志记录是禁用的。通过在创建 Fastify 实例时传递 { logger: true }{ logger: { level: 'info' } } 来启用它。请注意,如果日志记录被禁用,则无法在运行时重新启用。

为了实现这一目的使用了 abstract-logging  包。

由于 Fastify 专注于性能,因此它使用 pino  作为日志记录器,并且默认的日志级别设置为 'info'(如果启用的话)。

基本日志配置

启用生产 JSON 日志记录:

const fastify = require('fastify')({ logger: true })

环境特定配置

为了在本地开发、生产和测试环境中使用适当的日志记录配置,需要进行更多的配置:

const envToLogger = { development: { transport: { target: 'pino-pretty', options: { translateTime: 'HH:MM:ss Z', ignore: 'pid,hostname', }, }, }, production: true, test: false, } const fastify = require('fastify')({ logger: envToLogger[environment] ?? true // 如果映射中没有匹配项,则默认为 true })

⚠️ pino-pretty 需要作为开发依赖项安装。出于性能原因,默认情况下不会包含它。

使用方法

日志记录器可以在路由处理程序中如下使用:

fastify.get('/', options, function (request, reply) { request.log.info('关于当前请求的一些信息') reply.send({ hello: 'world' }) })

在路由处理程序之外触发新的日志,可以使用 Fastify 实例中的 Pino 实例:

fastify.log.info('发生了重要的事情!');

传递日志记录选项

要向日志记录器传递选项,请将它们提供给 Fastify。有关可用选项的详细信息,请参阅 Pino 文档 

要指定文件目标路径,可以使用:

const fastify = require('fastify')({ logger: { level: 'info', file: '/path/to/file' // 将使用 pino.destination() } }) fastify.get('/', options, function (request, reply) { request.log.info('关于当前请求的一些信息') reply.send({ hello: 'world' }) })

要向 Pino 实例传递自定义流,请在日志对象中添加 stream 字段:

const split = require('split2') const stream = split(JSON.parse) const fastify = require('fastify')({ logger: { level: 'info', stream: stream } })

高级日志配置

请求ID跟踪

默认情况下,Fastify 为每个请求添加一个 ID 以方便追踪。如果设置了 requestIdHeader 选项并且相应的头部存在,则使用该头部的值;否则生成一个新的增量 ID。有关自定义选项,请参阅 Fastify 工厂配置 requestIdHeader 和 Fastify 工厂配置 genReqId

序列化器

默认的日志记录器使用标准序列化器来处理具有 reqreserr 属性的对象。req 对象是 Fastify 的 Request 对象,而 res 对象是 Fastify 的 Reply 对象。可以通过自定义序列化器来自定义此行为。

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

例如,可以使用以下方法记录响应负载和头信息(不推荐):

const fastify = require('fastify')({ logger: { transport: { target: 'pino-pretty' }, serializers: { res (reply) { // 默认行为 return { statusCode: reply.statusCode } }, req (request) { return { method: request.method, url: request.url, path: request.routeOptions.url, parameters: request.params, // 在日志中包含头信息可能会违反隐私法律,例如 GDPR。使用 "redact" 选项来移除敏感字段。 headers: request.headers }; } } } });

ℹ️ 注意:在某些情况下,传递给 res 序列化器的 Reply 对象可能无法完全构建。编写自定义 res 序列化器时,请检查除始终存在的 statusCode 属性之外的所有属性是否存在。例如,在调用之前验证 getHeaders 的存在:

const fastify = require('fastify')({ logger: { transport: { target: 'pino-pretty' }, serializers: { res (reply) { // 默认行为 return { statusCode: reply.statusCode, headers: typeof reply.getHeaders === 'function' ? reply.getHeaders() : {} } }, } } });

ℹ️ 注意:req 方法内无法序列化请求体,因为当子日志器创建时会序列化请求。此时,请求体尚未解析。

要记录 req.body 可以使用以下方法:

app.addHook('preHandler', function (req, reply, done) { if (req.body) { req.log.info({ body: req.body }, '已解析的请求体') } done() })

ℹ️ 注意:确保序列化器永远不会抛出错误,因为这会导致 Node 进程退出。更多相关信息请参阅 Pino 文档 

任何其他日志器(除了 Pino)都会忽略此选项。

使用自定义日志记录器

可以通过传递 loggerInstance 参数来提供一个自定义的日志实例。该日志必须符合 Pino 接口,包含以下方法:info, error, debug, fatal, warn, trace, silent, child 和一个字符串属性 level

示例:

const log = require('pino')({ level: 'info' }) const fastify = require('fastify')({ loggerInstance: log }) log.info('不包含请求信息') fastify.get('/', function (request, reply) { request.log.info('包含请求信息,但与 `log` 是同一个日志实例') reply.send({ hello: 'world' }) })

当前请求的日志实例在生命周期的每个部分都可用。

日志屏蔽

Pino  支持低开销的日志屏蔽功能,用于隐藏记录日志中特定属性的值。例如,为了安全起见,可以记录所有 HTTP 头信息,但不包括 Authorization 头:

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, host: request.host, remoteAddress: request.ip, remotePort: request.socket.remotePort } } } } })

更多详情请参阅 https://getpino.io/#/docs/redaction。