Logging com Pino
Configure o Pino através do Fastify para logs JSON estruturados com contexto de requisição e performance pronta para produção.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import Fastify from "fastify";
const app = Fastify({
logger: {
level: process.env.LOG_LEVEL ?? "info",
redact: ["req.headers.authorization", "req.body.password"],
serializers: {
req(req) {
return { method: req.method, url: req.url, id: req.id };
},
},
},
});
app.get("/users", async (req) => {
req.log.info({ action: "list_users" });
return [];
});Quando usar isso: Todo serviço Fastify. Pino é o padrão e a escolha certa para logging JSON em produção.
Exemplo de Trabalho
import Fastify from "fastify";
const app = Fastify({
logger: {
level: "info",
timestamp: () => `,"time":"${new Date().toISOString()}"`,
redact: {
paths: ["req.headers.authorization", "req.headers.cookie", "body.password"],
censor: "[REDACTED]",
},
},
genReqId: (req) => req.headers["x-request-id"] as string ?? crypto.randomUUID(),
requestIdLogLabel: "requestId",
});
app.addHook("onRequest", async (req) => {
req.log.info({ event: "request_start" });
});
app.addHook("onResponse", async (req, reply) => {
req.log.info({
event: "request_complete",
statusCode: reply.statusCode,
responseTime: reply.elapsedTime,
});
});
app.get("/users/:id", async (req) => {
req.log.info({ userId: req.params.id, action: "get_user" });
return { id: (req.params as { id: string }).id };
});O que isso demonstra:
- Logs JSON estruturados com propagação de ID de requisição
- Redação de cabeçalhos sensíveis e campos de corpo
- Logging do ciclo de vida da requisição via hooks
reply.elapsedTimepara tempo de resposta
Mergulho Profundo
Como Funciona
- Pino escreve linhas JSON para stdout (um log por linha)
req.logé um logger filho vinculado ao ID da requisição- Pino é assíncrono: serializa em uma thread de worker (impacto mínimo na thread principal)
- Agregadores de logs (Datadog, Loki, CloudWatch) analisam linhas JSON nativamente
Níveis de Log
| Nível | Usar para |
|---|---|
fatal | Processo inutilizável |
error | Erros tratados, operações falhas |
warn | Estado degradado, novas tentativas |
info | Ciclo de vida da requisição, eventos de negócio |
debug | Diagnósticos de desenvolvimento |
trace | Internos verbosos |
Impressão Bonita para Desenvolvimento
const app = Fastify({
logger: {
transport: process.env.NODE_ENV === "development"
? { target: "pino-pretty", options: { colorize: true } }
: undefined,
},
});Nunca use pino-pretty em produção. Ele bloqueia a thread principal.
Armadilhas
- Logging do
req.bodycompleto - PII em armazenamento de logs. Correção: redigir caminhos; registrar apenas campos necessários. - pino-pretty em produção - mata a taxa de transferência e bloqueia o loop de eventos. Correção: JSON para stdout apenas.
- ID de requisição ausente - não é possível correlacionar logs entre serviços. Correção:
genReqIda partir do cabeçalhox-request-id. console.logao lado do Pino - logs não estruturados misturados com JSON. Correção: proibirconsole.logem regras de lint.- Logging dentro de loops apertados - o volume de logs explode. Correção: amostrar ou agregar.
- Não registrar contexto de erro -
log.error(err)sem o serializadorerrperde o stack. Correção:req.log.error({ err }, "message").
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Pino standalone | App Express ou Node puro | Já está no Fastify (embutido) |
| Winston | Mandato da equipe para transports Winston | Fastify novo (Pino é mais rápido) |
| OpenTelemetry logs | Traces + logs + métricas unificados | API simples que precisa apenas de logs de requisição |
| morgan (Express) | Apenas logs de acesso Express | Projeto Fastify |
FAQs
Por que o Fastify usa Pino por padrão?
Pino é o logger JSON Node mais rápido. Ele se alinha com o foco em performance do Fastify e produz logs estruturados sem configuração.
Como adiciono o ID do usuário a cada log?
Em um hook onRequest após a autenticação: req.log = req.log.child({ userId: req.user.id }).
Posso enviar logs para o Datadog?
Registre JSON para stdout; use o agente Datadog para coletar logs de contêineres. Ou use pino-datadog-transport (adiciona latência).
Como isso se relaciona com AsyncLocalStorage?
O ID da requisição via genReqId cobre o contexto HTTP. Para workers não-HTTP, use AsyncLocalStorage. Veja AsyncLocalStorage para Contexto.
Devo registrar corpos de requisição?
Apenas em desenvolvimento, com redação. Produção: registre método, caminho, status, duração e ID da requisição.
Como silenciar logs de verificação de saúde?
disableRequestLogging personalizado por rota ou filtrar em onResponse quando o caminho for /health.
O NestJS usa Pino?
Via pacote nestjs-pino. O adaptador Fastify integra-se naturalmente.
E a correlação com serviços downstream?
Encaminhe x-request-id em chamadas HTTP de saída. Registre o mesmo ID em ambos os serviços.
Relacionados
- Princípios de Logging - princípios de logging estruturado
- IDs de Correlação de Requisição - tracing distribuído
- Redação de PII e Conformidade - regras de redação
- Fundamentos do Fastify - configuração do logger
- Melhores Práticas do Fastify - checklist da seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (LTS Ativo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.