Logging e monitoramento com Claude Code: guia prático de observabilidade
Estruture logs, OpenTelemetry, alertas e handoff de incidentes com Claude Code.
Defina primeiro o contrato operacional
Se você pedir ao Claude Code “adicione logs”, provavelmente receberá mais alguns console.log. Isso ajuda no ambiente local, mas não resolve um incidente de produção. Uma base real de logging e monitoramento precisa responder qual ação do usuário iniciou a requisição, qual requestId acompanhou o fluxo, qual serviço ficou lento, qual dependência falhou e quais dados podem ser passados com segurança para a próxima pessoa de plantão.
O OpenTelemetry Observability primer explica observabilidade como a capacidade de entender um sistema por sinais externos e responder perguntas novas. A página What is OpenTelemetry? também deixa claro que OpenTelemetry é uma camada neutra de instrumentação, não o backend de observabilidade. Este artigo transforma esse conceito em prompts para Claude Code, snippets copiáveis, regras de alerta, revisão de dashboard e handoff de incidentes.
Masa testou isso em uma pequena API de checkout. O primeiro prompt dizia apenas “deixe os logs mais detalhados”. Claude Code sugeriu gravar o corpo completo da requisição quando um pagamento falhasse. Não havia número de cartão, mas poderia haver email, cupom, endereço e notas do cliente. Depois que o prompt passou a listar campos proibidos, requestId, traceparent e testes de redaction, a revisão ficou muito mais objetiva.
| Sinal | Pergunta | Restrição para Claude Code |
|---|---|---|
| Logs | O que aconteceu | JSON, campos fixos, PII mascarada |
| Métricas | Quão grave é | rate, p95, taxa de erro |
| Traces | Onde o tempo foi gasto | propagar traceparent, nomear spans |
| Health checks | A dependência funciona | status e latency por dependência |
Prompt seguro e regras em CLAUDE.md
A documentação de memória do Claude Code explica que CLAUDE.md fornece instruções de projeto, usuário ou organização no início das sessões. Para observabilidade, coloque ali níveis de log, nomes de campos, campos proibidos, comandos de teste, dashboards e formato de incidente. Combine isso com boas práticas de CLAUDE.md e com as páginas oficiais de Claude Code permissions e hooks.
Claude Code task:
- Add observability to the checkout API only.
- Keep all changes inside src/checkout and tests/checkout.
- Use structured JSON logs with requestId and traceparent.
- Never log passwords, tokens, cookies, email, phone, address,
raw prompt text, or full request/response bodies.
- Add tests proving redaction and requestId propagation.
- Add a /healthz report with database and cache latency.
- Add alert rules for 5xx rate, p95 latency, and redaction failure.
- Show a diff summary and remaining manual checks at the end.
Esse prompt é estreito de propósito. Ele não deixa Claude Code escolher uma plataforma nova, alterar rotas sem relação ou receber logs crus de produção. Ele pede um diff operacional que pode ser revisado, testado e revertido.
Logs estruturados e IDs de correlação
O OWASP Logging Cheat Sheet trata logging como recurso de segurança. Ele deve ter revisão, testes, controle de acesso, proteção contra log injection, comportamento sob falha e cuidado com armazenamento. Por isso, peça ao Claude Code testes de redaction e propagação, não apenas mensagens de texto.
O logger abaixo não usa dependências. Salve como structured-logger.mjs e rode com Node.js 18 ou superior.
import { randomUUID } from "node:crypto";
const rank = { debug: 10, info: 20, warn: 30, error: 40 };
const current = process.env.LOG_LEVEL || "info";
const threshold = rank[current] ?? rank.info;
const secretKeys = [
"password",
"token",
"authorization",
"cookie",
"set-cookie",
"apikey",
];
function cleanText(value) {
return String(value).replace(/[\r\n\t]/g, " ").slice(0, 500);
}
function redact(value) {
if (Array.isArray(value)) return value.map(redact);
if (!value || typeof value !== "object") return value;
return Object.fromEntries(
Object.entries(value).map(([key, item]) => {
if (secretKeys.includes(key.toLowerCase())) {
return [key, "[REDACTED]"];
}
return [key, redact(item)];
}),
);
}
export function log(level, message, fields = {}) {
if ((rank[level] ?? 99) < threshold) return;
const entry = {
ts: new Date().toISOString(),
level,
service: process.env.SERVICE_NAME || "checkout-api",
env: process.env.NODE_ENV || "development",
requestId: fields.requestId || randomUUID(),
msg: cleanText(message),
...redact(fields),
};
process.stdout.write(`${JSON.stringify(entry)}\n`);
}
log("info", "payment accepted", {
requestId: "req_demo_001",
userId: "user_123",
amount: 4980,
token: "sk_live_should_not_leak",
});
Em aplicações web, devolva o ID da requisição no header e guarde-o no contexto assíncrono. A especificação W3C Trace Context descreve como criar ou propagar traceparent. Use x-request-id para correlação da aplicação e traceparent para tracing distribuído.
import { AsyncLocalStorage } from "node:async_hooks";
import { randomUUID } from "node:crypto";
import type { Request, Response, NextFunction } from "express";
import { log } from "./structured-logger";
type RequestContext = {
requestId: string;
traceparent?: string;
userId?: string;
};
const storage = new AsyncLocalStorage<RequestContext>();
export function getRequestContext() {
return storage.getStore();
}
export function requestContext(
req: Request,
res: Response,
next: NextFunction,
) {
const started = performance.now();
const user = (req as Request & { user?: { id?: string } }).user;
const requestId =
req.get("x-request-id") ||
req.get("cf-ray") ||
randomUUID();
const context = {
requestId,
traceparent: req.get("traceparent"),
userId: user?.id,
};
res.setHeader("x-request-id", requestId);
storage.run(context, () => {
res.on("finish", () => {
const durationMs = Math.round(performance.now() - started);
const level = res.statusCode >= 500
? "error"
: res.statusCode >= 400
? "warn"
: "info";
log(level, "http request completed", {
requestId,
method: req.method,
path: req.path,
statusCode: res.statusCode,
durationMs,
});
});
next();
});
}
Mantenha os níveis simples: debug para detalhes locais, info para eventos normais importantes, warn para risco recuperável e error para investigação humana. Claude Code não deve mudar o fluxo de negócio se uma chamada de log falhar.
Fundamentos de OpenTelemetry
OpenTelemetry padroniza sinais antes de enviá-los ao backend que sua equipe já usa. Para Node.js, confira os guias oficiais JavaScript Node.js e JavaScript exporters.
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-proto \
@opentelemetry/exporter-metrics-otlp-proto \
@opentelemetry/sdk-metrics
const opentelemetry = require("@opentelemetry/sdk-node");
const {
getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");
const {
OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-proto");
const {
OTLPMetricExporter,
} = require("@opentelemetry/exporter-metrics-otlp-proto");
const {
PeriodicExportingMetricReader,
} = require("@opentelemetry/sdk-metrics");
process.env.OTEL_SERVICE_NAME ||= "checkout-api";
const endpoint =
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
"http://localhost:4318";
const sdk = new opentelemetry.NodeSDK({
traceExporter: new OTLPTraceExporter({
url: `${endpoint}/v1/traces`,
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: `${endpoint}/v1/metrics`,
}),
exportIntervalMillis: 30000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
process.on("SIGTERM", () => {
sdk.shutdown().finally(() => process.exit(0));
});
flowchart LR
A["Acao do usuario"] --> B["Aplicacao"]
B --> C["Log estruturado"]
B --> D["Metrica"]
B --> E["Trace span"]
C --> F["Armazem de logs"]
D --> G["Regra de alerta"]
E --> H["Backend de traces"]
F --> I["Handoff de incidente"]
G --> I
H --> I
Health checks e alertas
Um bom health check não é apenas 200 OK. Ele verifica banco de dados, cache, fila e APIs externas separadamente, devolve latency e não mostra secrets.
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error("timeout")), ms);
});
}
export async function buildHealthReport(checks) {
const started = Date.now();
const results = {};
for (const [name, check] of Object.entries(checks)) {
const before = Date.now();
try {
await Promise.race([check(), timeout(800)]);
results[name] = {
status: "ok",
latencyMs: Date.now() - before,
};
} catch (error) {
const message =
error instanceof Error ? error.message : String(error);
results[name] = {
status: "fail",
latencyMs: Date.now() - before,
reason: message.slice(0, 120),
};
}
}
const failed = Object.values(results)
.filter((item) => item.status === "fail")
.length;
return {
status: failed ? "degraded" : "ok",
uptimeSec: Math.round(process.uptime()),
totalLatencyMs: Date.now() - started,
checks: results,
};
}
Alertas devem usar taxas e percentis, não uma única linha ruidosa.
groups:
- name: checkout-api
rules:
- alert: CheckoutHigh5xxRate
expr: |
sum(rate(http_requests_total{
service="checkout-api",
status_code=~"5.."
}[5m]))
/
sum(rate(http_requests_total{
service="checkout-api"
}[5m])) > 0.02
for: 10m
labels:
severity: page
annotations:
summary: "Checkout 5xx rate is above 2%"
- alert: CheckoutP95LatencyHigh
expr: |
histogram_quantile(
0.95,
sum by (le) (
rate(http_request_duration_seconds_bucket{
service="checkout-api"
}[5m])
)
) > 1.5
for: 15m
labels:
severity: ticket
annotations:
summary: "Checkout p95 latency is above 1.5s"
Três casos de uso concretos
O primeiro caso é uma API de checkout ecommerce. Guarde orderId, requestId, paymentProvider e amount, mas nunca cartão, email, endereço ou token. Separe alertas de taxa 5xx, falha de pagamento e p95 do provedor.
O segundo é um painel admin SaaS. Login, mudança de permissão, convite de membro e alteração de plano entram em audit logs. Corpo do email de convite e notas privadas não entram. Peça ao Claude Code para separar audit logs de logs da aplicação, distinguir actor ID e target user ID, e adicionar testes RBAC.
O terceiro é um site de mídia ou blog CMS. Acompanhe publicação, clique em CTA, sucesso de formulário, falha de geração de imagem e tradução pendente. Page views sozinhas não melhoram receita. Separe cta_click e generate_lead, e conecte com o guia de analytics com Claude Code.
Se houver microserviços, leia também o guia de microservices. Quando service.name e labels de ambiente variam, consultar OpenTelemetry vira trabalho manual.
Falhas comuns e handoff
Os problemas previsíveis são: registrar request body inteiro, usar texto livre difícil de buscar, criar IDs demais, ter /healthz que não checa dependências e alertas sem dono. Com Claude Code há ainda o risco de colar logs crus no prompt. Antes de pedir ajuda, mascare logs, compartilhe métricas agregadas e envie apenas IDs curtos.
{
"incident_id": "INC-2026-06-02-001",
"severity": "SEV2",
"owner": "oncall-api",
"customer_impact": "Checkout errors for some card payments",
"first_seen": "2026-06-02T09:15:00+09:00",
"request_ids": ["req_7f3a", "req_8b21"],
"trace_ids": ["7bba9f33312b3dbb8b2c2c62bb7abe2d"],
"dashboards": ["Checkout API overview"],
"current_hypothesis": "Payment provider latency spike",
"actions_taken": ["Disabled checkout_v2 feature flag"],
"next_checks": ["Compare p95 latency by region"],
"do_not_do": ["Do not paste raw customer data into prompts"]
}
Para investigação segura, combine com o permissions guide.
Revisão, CTA e verificação
Revise dashboards toda semana: cinco erros principais, p95, crescimento de logs, falsos positivos, falhas de redaction e incidentes abertos. Uma vez por mês, pegue um incidente real e veja se logs, métricas e traces teriam levado uma nova pessoa de plantão à causa mais rápido.
Para começar sozinho, use o cheatsheet gratuito. Para prompts e materiais reutilizáveis, veja a products page. Se sua equipe precisa de padrões de logging, CLAUDE.md, permissões, CI e fluxo de incidentes em um repositório real, Claude Code training and consultation é o próximo passo.
Ao testar este fluxo, o maior ganho veio de escrever os campos proibidos antes do logger. structured-logger.mjs transformou token em [REDACTED] e normalizou quebras de linha. Simulando falha de cache, o health report virou degraded; com requestId e traceId no handoff, a revisão ficou bem mais curta.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Permission receipt no Claude Code: escopo, prova e rollback
Padrão de permission receipt para Claude Code: ações permitidas, limites de aprovação, comandos de prova, rollback e CTA de receita.
Agent Harness seguro para Claude Code e Codex: permissoes, verificacao e rollback
Monte uma base segura para agentes com Claude Code e Codex usando politicas, plano, verificacao e recuperacao.
Subagentes no Claude Code: guia prático para delegar trabalho com segurança
Guia prático de subagentes no Claude Code para dividir artigos e código: regras, prompts, riscos e checklist.