示例说明
日志的价值取决于能否被检索与关联。Fastify 内建 Pino 集成,每个请求自动携带上下文日志对象;genReqId 让上游网关的请求 ID 透传贯穿全链路;redact 在日志落地前擦除授权头与密码字段,避免审计事故。onResponse 钩子基于耗时阈值输出慢请求告警,是低成本的性能哨兵。
技术要点
- genReqId 复用上游请求头或生成 UUID
- redact 配置拦截敏感字段写入
- elapsedTime 支撑慢请求阈值告警
代码解读
构造 Fastify 实例时传入 logger 配置对象,level 从环境变量读取并默认 info;serializers 自定义响应日志只保留状态码与耗时,避免序列化整个响应体。onResponse 钩子在响应完成后判断耗时超过 1 秒即以 warn 级别记录目标 URL。
使用建议
生产环境应输出 JSON 到 stdout 由采集器接管,pino-pretty 仅用于本地开发。redact 路径要随新增敏感字段同步维护。请求 ID 应最终回写到响应头,方便用户报障时定位。
代码示例
import Fastify from 'fastify';
const app = Fastify({
logger: {
level: process.env.LOG_LEVEL ?? 'info',
genReqId: (req) => req.headers['x-request-id'] ?? crypto.randomUUID(),
redact: {
paths: ['req.headers.authorization', 'req.headers.cookie', '*.password'],
censor: '[REDACTED]',
},
serializers: {
res(reply) {
return { statusCode: reply.statusCode, responseTime: reply.elapsedTime };
},
},
transport: { target: 'pino-pretty', options: { colorize: true } },
},
});
app.addHook('onResponse', async (request, reply) => {
if (reply.elapsedTime > 1000) {
request.log.warn({ url: request.url }, 'slow request');
}
});
app.get('/health', async () => ({ status: 'ok' }));
await app.listen({ port: 3000, host: '0.0.0.0' });