Registro Guardado Não É Pergunta Respondida
O cliente diz que o pagamento falhou ontem e você tem registro de tudo, mas não consegue responder o que aconteceu. Como um identificador por requisição liga os logs e transforma uma investigação em uma consulta.
Renan Montanari
Engenheiro de Software Full-Stack
Introdução: Tudo Foi Registrado. Nada Foi Respondido.
O cliente diz que tentou pagar ontem, por volta das três da tarde, e não conseguiu.
Você tem registro de tudo. Mesmo assim, não consegue responder o que aconteceu.
Porque registro guardado não é a mesma coisa que pergunta respondida.
Por Que Os Logs Não Conversam Entre Si
Um log costuma contar o que aconteceu dentro de uma peça do sistema, isolada. Aquele pagamento, porém, passou por várias peças:
- A tela, que enviou a requisição.
- Seu servidor, que validou e processou.
- O meio de pagamento, que autorizou ou recusou.
- O servidor de novo, que recebeu a resposta e atualizou o pedido.
São quatro registros, em quatro lugares, sem nada que ligue um ao outro. Procurar “o pagamento desse cliente” significa cruzar horários, IDs de pedido e palpites.
A Correção Pequena Que Muda Tudo
A solução é simples de descrever: cada requisição recebe um identificador único, e esse identificador aparece em todo registro que ela gerar.
Esse identificador costuma ser chamado de correlation ID ou request ID. Ele é criado na entrada do sistema e propagado para tudo o que a requisição toca.
import { randomUUID } from 'node:crypto';
import { AsyncLocalStorage } from 'node:async_hooks';
// Contexto que acompanha a requisição, inclusive em operações assíncronas
export const requestContext = new AsyncLocalStorage<{ requestId: string }>();
// Middleware: cria o ID na entrada, ou reaproveita o que veio do cliente ou do gateway
export function requestIdMiddleware(req, res, next) {
const requestId = req.headers['x-request-id'] ?? randomUUID();
res.setHeader('x-request-id', requestId);
requestContext.run({ requestId }, () => next());
}
// Logger que sempre inclui o ID, sem depender de quem chama
export function log(level: 'info' | 'error', message: string, data: object = {}) {
const store = requestContext.getStore();
console[level](JSON.stringify({
message,
requestId: store?.requestId,
timestamp: new Date().toISOString(),
...data,
}));
}Repare no que isso resolve:
- Quem escreve o log não precisa lembrar de passar o ID. O contexto acompanha a requisição sozinho.
- Operações assíncronas herdam o contexto. O
AsyncLocalStoragemantém o ID mesmo depois deawait. - O ID volta para o cliente no header de resposta. Se ele mandar o print de um erro, o ID está ali.
Em projetos maiores, esse papel costuma ser feito por ferramentas de tracing distribuído, como OpenTelemetry. A ideia é a mesma. O código acima mostra o princípio sem depender de infraestrutura.
Quando “Buscar o Cliente” Vira Dez Segundos
Com o ID presente em todo registro, a investigação muda de forma:
- Antes: cruzar logs de três sistemas, filtrar por horário, tentar adivinhar qual pedido era.
- Depois: pegar o ID que o cliente enviou e filtrar. Todos os registros da requisição aparecem em ordem.
A diferença não é de ferramenta cara. É de decisão tomada antes de o problema acontecer.
Trade-offs: O ID Não Pode Vazar Nada
Alguns cuidados:
- O ID não deve conter dado pessoal. Use UUID aleatório, não CPF, e-mail ou número de pedido.
- Aceitar o ID de fora exige cuidado. Se o cliente ou um gateway enviar um
x-request-id, valide o formato antes de registrar. Caso contrário, alguém pode poluir os logs. - Mensagem de erro para o usuário deve ser genérica. O ID ajuda o suporte, mas não precisa revelar detalhes internos do erro.
Checklist: Sua Investigação Leva Dez Minutos?
- Cada requisição recebe um identificador único?
- Esse identificador aparece em todos os logs que a requisição gera?
- Ele é propagado para chamadas a serviços externos e filas?
- O cliente ou o suporte consegue ver esse ID quando algo falha?
- Você consegue reconstruir o caminho de uma requisição em minutos?
Conclusão: Pergunte Antes do Incidente
Quanto tempo o seu time levaria para descobrir por que um cliente específico falhou ontem?
Se a resposta for “horas”, o problema não é falta de registro. É falta de ligação entre os registros.
Um ID por requisição custa pouco para implementar, e transforma uma investigação em uma consulta.
Falar comigo