Regras de Logging & Observabilidade
Logs estruturados e IDs de correlação tornam os serviços Node depuráveis em produção sem SSH e grep de strings não estruturadas.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import pino from "pino";
import { randomUUID } from "node:crypto";
export const logger = pino({ level: process.env.LOG_LEVEL ?? "info" });
app.use((req, res, next) => {
const requestId = req.headers["x-request-id"]?.toString() ?? randomUUID();
res.setHeader("X-Request-Id", requestId);
req.log = logger.child({ requestId });
next();
});Quando usar isso:
- Todo serviço HTTP de produção e worker em background.
- Chamadas entre serviços precisam de cadeias de requisição rastreáveis.
- Conformidade exige trilhas de auditoria sem vazamento de PII.
Exemplo de Trabalho
// src/logging/logger.ts
import pino from "pino";
export const rootLogger = pino({
level: process.env.LOG_LEVEL ?? "info",
redact: ["req.headers.authorization", "password", "creditCard"],
});
export type AppLogger = pino.Logger;// src/middleware/request-context.ts
import { randomUUID } from "node:crypto";
import type { Request, Response, NextFunction } from "express";
import { rootLogger } from "../logging/logger.js";
declare global {
namespace Express {
interface Request {
log: import("pino").Logger;
requestId: string;
}
}
}
export function requestContext(req: Request, res: Response, next: NextFunction) {
const requestId = (req.headers["x-request-id"] as string) ?? randomUUID();
req.requestId = requestId;
res.setHeader("X-Request-Id", requestId);
req.log = rootLogger.child({ requestId, method: req.method, path: req.path });
req.log.info("request started");
res.on("finish", () => req.log.info({ status: res.statusCode }, "request finished"));
next();
}// src/services/billing.ts
export async function charge(log: AppLogger, customerId: string, cents: number) {
log.info({ customerId, cents }, "charge started");
// ...
log.info({ customerId }, "charge completed");
}O que isso demonstra:
- Logs JSON via Pino com redação de campos.
X-Request-Idde entrada respeitado; gerado se ausente.- Logger filho carrega
requestIdpela camada de serviço.
Mergulho Profundo
Como Funciona
- Logs estruturados são linhas JSON que podem ser ingeridas por Loki, CloudWatch, Datadog.
- ID de correlação propaga para chamadas HTTP de saída via cabeçalho em clientes internos.
- Níveis de log:
errorpara falhas que precisam de ação,warnpara degradação,infopara ciclo de vida da requisição,debugapenas para desenvolvimento. - Métricas (histograma de duração da requisição, taxa de erro) complementam os logs para SLOs.
Campos Obrigatórios
| Campo | Fonte |
|---|---|
requestId | Cabeçalho ou UUID |
level | Padrão do Pino |
msg | Evento legível por humanos |
time | Timestamp ISO automático |
service | name na configuração do logger |
Notas de TypeScript
- Tipo de aumento
req.logpara Express; Fastify usareq.logintegrado com integração Pino. - Nunca faça log do
req.bodycompleto - registre apenas IDs e nomes de ação.
Armadilhas
- console.log em produção - Não estruturado, sem níveis, sem redação. Correção: ESLint
no-consoleemsrc/. - Correlação ausente em workers - Tarefas de fila não rastreáveis. Correção: passe
requestIdno payload da tarefa do manipulador HTTP. - Log de PII - Violações do GDPR. Correção: oculte caminhos; hasheie IDs de usuário nos logs, se necessário.
- Logs de debug sempre ativos - Custo e ruído em produção. Correção:
LOG_LEVEL=infopadrão; depure via alternância dinâmica com expiração. - Log dentro de loops apertados - Picos de volume de log. Correção: agregue ou amostre; registre contagens de resumo.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| OpenTelemetry traces | Rastreamento distribuído completo | MVP de serviço único minúsculo |
| Winston | Padrão legado da equipe | Greenfield (Pino é mais rápido no Node) |
| AsyncLocalStorage context | RequestId implícito sem req.log | Aplicativos Express simples (middleware suficiente) |
FAQs
Pino é obrigatório?
Não, mas o logging estruturado em JSON é obrigatório. Pino é a escolha comum para Node 24; Fastify integra nativamente.
Como propagar requestId para fora?
await fetch(url, { headers: { "X-Request-Id": requestId } });E sobre NestJS?
Use nestjs-pino ou OTel; as mesmas regras de cabeçalho de correlação se aplicam.
Erros devem incluir stack traces?
Sim em logs de error no lado do servidor; nunca retorne stacks para clientes de API em produção.
Amostragem de logs?
Amostre debug/info em health checks de alto RPS; nunca amostre logs de erro.
Formato de log do worker?
Mesmo esquema JSON com campos jobId e requestId para junção de rastros.
Métricas vs logs?
Métricas para dashboards de SLO; logs para depuração de incidentes. Ambos necessários para operações maduras.
Por quanto tempo reter logs?
Política da organização; tipicamente 30-90 dias quentes, arquivamento frio mais longo para domínios de auditoria.
Apenas stdout?
Sim no Kubernetes - coletores raspam stdout; sem rotação de arquivos no contêiner.
Logging de health check?
Não registre cada /health em info em produção; use debug ou exclua o caminho no middleware.
Relacionados
- Node Project Rules Checklist - regras 14-15
- Security Rules - sobreposição de redação de PII
- API Rules - resposta de erro vs detalhe do log
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.