System Design

Observabilidade

A capacidade de entender o estado interno de um sistema a partir das suas saídas externas — sem precisar implantar novo código para investigar um problema. Os três pilares são Logs, Métricas e Distributed Tracing.

Intenção

Observabilidade é a propriedade de um sistema que permite inferir seu estado interno apenas analisando as saídas que ele produz. Em produção você não tem acesso a um debugger — o que você tem são logs, métricas e traces. Sem instrumentação adequada, investigar um incidente se torna adivinhação.

O termo vem da teoria de controle: um sistema é "observável" se seu estado pode ser determinado a partir das suas saídas externas ao longo do tempo. No contexto de software, isso significa que quando algo dá errado em produção, você consegue responder: o quê aconteceu? (logs), com que frequência e intensidade? (métricas) e onde exatamente no fluxo ocorreu? (tracing). Sem as três respostas, a investigação opera no escuro.

Problema

Sistemas distribuídos modernos — com dezenas de microsserviços, chamadas síncronas e assíncronas, filas, caches e bancos de dados — são inerentemente opacos. Uma requisição do usuário pode atravessar oito serviços antes de retornar resposta; quando falha, a causa pode estar em qualquer um deles ou na interação entre eles.

  • Logs sem correlação: quando cada serviço escreve logs independentes sem identificador comum, correlacionar o comportamento de uma requisição específica entre serviços exige horas de grep manual.
  • Métricas só de média: a média de latência pode parecer saudável enquanto 1% dos usuários experimenta timeouts de 10 segundos. Percentis (p95, p99) revelam o que a média esconde.
  • Sem tracing, sem localização do gargalo: saber que "o serviço de pedidos ficou lento" é diferente de saber que "a query SQL de busca de estoque dentro do serviço de pedidos levou 800 ms naquela chamada específica".

Como funciona

Logs — registro de eventos discretos

Logs são registros de eventos com timestamp, nível de severidade e dados contextuais. Logs estruturados em JSON são preferíveis a texto livre porque permitem filtragem, indexação e consulta em ferramentas como Elasticsearch, Loki ou CloudWatch Logs Insights.

Cada log deve incluir um Correlation ID (também chamado de TraceID) — um identificador único da requisição original que permanece constante enquanto ela atravessa todos os serviços. Sem esse ID, correlacionar logs de diferentes serviços é inviável em qualquer volume real de tráfego.

// Log estruturado — filtrável e indexável
{"timestamp":"2024-01-15T14:23:01Z","level":"ERROR",
 "service":"order-svc","msg":"payment timeout",
 "traceId":"abc-123","userId":"u-456","durationMs":5032}

// Log texto livre — difícil de filtrar programaticamente
[ERROR] 2024-01-15 14:23:01 Payment timeout for user u-456

Níveis de severidade: DEBUG (detalhes de desenvolvimento), INFO (eventos de negócio normais), WARN (situação anormal mas recuperável), ERROR (falha que requer atenção). Em produção, use INFO como padrão — DEBUG gera volume e custo desproporcionais.

Métricas — valores numéricos agregados

Métricas são observações numéricas coletadas ao longo do tempo e agregadas em séries temporais. São mais eficientes que logs para análise de tendências e alertas. Os três tipos fundamentais:

  • Counter: valor monotônico que só aumenta. Ex.: total de requisições, total de erros. Útil para calcular taxas (rate() em Prometheus). Nunca decresce — reinicia apenas quando o processo reinicia.
  • Gauge: valor instantâneo que sobe e desce. Ex.: conexões abertas, memória usada, tamanho de fila. Captura o estado atual do sistema.
  • Histogram: distribui observações em buckets configuráveis e permite calcular percentis (p50, p95, p99). Essencial para latência, porque a média esconde o tail — p99 pode ser 100x a média em sistemas com distribuição de cauda longa.

O RED Method (por Tom Wilkie) define as três métricas essenciais para qualquer serviço: Rate (volume de requisições por segundo), Errors (taxa de erros) e Duration (distribuição de latência). Qualquer SLO começa por aqui. O stack mais comum em open source é Prometheus para coleta e Grafana para visualização.

Distributed Tracing — rastreamento de requisições

Distributed Tracing acompanha uma requisição enquanto ela atravessa múltiplos serviços. Cada unidade de trabalho — uma chamada HTTP, uma query de banco, uma operação de cache — é um Span: um intervalo com tempo de início, duração, atributos e referência ao span pai. Spans relacionados formam um Trace.

O TraceID é propagado entre serviços via headers HTTP. O padrão atual é o W3C Trace Context com o header traceparent:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^
             ver        trace-id (16 bytes)        parent-span-id  flags

OpenTelemetry é o padrão atual para instrumentação — uma API e SDK vendor-neutro que exporta telemetria para qualquer backend (Jaeger, Zipkin, Grafana Tempo, Datadog, Honeycomb). Adotar OpenTelemetry desde o início evita lock-in em ferramentas de observabilidade.

Em alta volumetria, coletar 100% dos traces é proibitivo em custo e overhead. Head-based sampling decide no início da requisição (ex.: 1–10% das requisições); tail-based sampling decide ao final, retendo 100% dos traces com erros e descartando os saudáveis — mais inteligente, mais complexo de implementar.

LOGS {"level":"INFO", "msg":"request started", "traceId":"abc-123"} {"level":"WARN", "msg":"slow query 800ms", "traceId":"abc-123"} {"level":"ERROR", "msg":"timeout 5032ms", "traceId":"abc-123"} {"level":"INFO", "status":500,"service":"order", "traceId":"abc-123"} DEBUG · INFO · WARN · ERROR JSON estruturado + Correlation ID MÉTRICAS p99 p95 p50 0 tempo → p99 p95 p50 (mediana) Counter · Gauge · Histogram RED Method: Rate · Errors · Duration DISTRIBUTED TRACING API GW Order Auth DB 320 ms 296 ms 88 ms 44 ms TraceID: abc-123 4 spans · header traceparent W3C OpenTelemetry · Jaeger · Zipkin Waterfall de spans por requisição Sampling 1–10% em alta volumetria TraceID: abc-123 O mesmo TraceID aparece nos Logs + Métricas + Trace — correlação é o que transforma três silos em observabilidade real
Os três pilares da observabilidade correlacionados pelo mesmo TraceID. Logs registram eventos discretos, Métricas revelam tendências e percentis, Distributed Tracing localiza o gargalo exato no fluxo entre serviços.

Correlação entre os três pilares

Os três pilares valem individualmente, mas a observabilidade real emerge quando estão correlacionados. O mesmo TraceID que aparece no log de erro permite navegar diretamente ao trace daquela requisição específica, ver quais spans estavam lentos e então ir às métricas de latência daquele serviço no mesmo instante. Essa navegação fluida (log → trace → métrica) é o que diferencia observabilidade de monitoramento tradicional.

Quando usar

  • Qualquer serviço em produção: os três pilares são pré-requisito, não diferencial. Comece com logs estruturados e métricas RED; adicione tracing depois.
  • Microsserviços e sistemas distribuídos: tracing é especialmente valioso quando uma requisição atravessa múltiplos serviços. É impossível correlacionar manualmente o comportamento de 10 serviços sem TraceID e cascata de spans.
  • Ao definir SLOs: os pilares fornecem os dados para medir SLIs — latência p99, taxa de erros, disponibilidade — e verificar se os objetivos estão sendo cumpridos.

Quando evitar (ou dimensionar com cuidado)

  • Tracing com 100% de sampling em alta volumetria: em serviços com 10 k req/s, armazenar todos os traces é proibitivo. Use head-based ou tail-based sampling. Alertas e métricas não precisam de sampling.
  • Logs DEBUG em produção continuamente: esse nível pode gerar GBs por hora e custo de ingestão desproporcional. Use INFO como padrão; DEBUG apenas em janelas específicas de investigação.

Prós e contras

Prós

  • Reduz drasticamente o MTTR: incidentes são investigados com dados concretos, não com suposições.
  • Permite SLOs baseados em dados reais — p99 de latência, taxa de erros, disponibilidade medida.
  • Tracing revela gargalos invisíveis em métricas de serviço isoladas.
  • OpenTelemetry como padrão evita lock-in em vendor de observabilidade.

Contras

  • Custo: armazenamento de logs e traces em alta volumetria pode ser significativo — requer gestão ativa de retenção e sampling.
  • Overhead de instrumentação: tracing adiciona latência (~1 ms) e consumo de CPU por requisição. Sampling mitiga, mas não elimina.
  • Complexidade de setup: configurar OpenTelemetry, Prometheus, Grafana e Jaeger e correlacionar tudo tem curva de aprendizado relevante.
  • Instrumentação retroativa é muito mais cara: adicionar observabilidade a código legado sem injeção de dependência é ordens de magnitude mais trabalhoso do que instrumentar ao desenvolver.

Armadilhas comuns

1. Logs em texto livre

Logs sem estrutura — [ERROR] Payment failed for user 12345 — são legíveis para humanos mas impossíveis de filtrar programaticamente. Quando você precisa de "todos os erros do usuário X nas últimas 2 horas" em um sistema com 1 k req/s, grep em texto livre não é uma estratégia viável. Use JSON estruturado com campos indexáveis desde o primeiro dia.

2. Métricas sem percentis — a armadilha da média

A média de latência pode estar saudável enquanto 1% dos usuários sofre timeouts de 30 segundos. Isso ocorre porque a média é sensível ao volume (muitas requisições rápidas diluem as lentas) mas insensível à cauda. Sempre monitore p95 e p99. Em SLAs voltados ao usuário, o percentil relevante costuma ser p99 — o "pior 1%" é o usuário mais impactado.

Prática: defina alertas no p99 de latência e na taxa de erros por rota. Média de latência como alerta gera tanto falsos negativos (não alertou quando devia) quanto falsos positivos (alertou por outliers isolados).

3. Tracing sem sampling — custo e overhead

Coletar 100% dos traces em um serviço com 5 000 requisições por segundo significa armazenar 432 milhões de traces por dia. Além do custo de armazenamento, a própria exportação de traces adiciona overhead de rede. Use sampling: head-based (1–10%) para casos gerais; tail-based (retém 100% dos traces com erros) para diagnóstico de falhas.

4. Observabilidade adicionada após o incidente

A instrumentação tem custo muito mais baixo quando implementada junto com o código. Adicionar observabilidade após o fato — especialmente em código legado sem injeção de dependência — é ordens de magnitude mais caro. Trate observabilidade como requisito funcional: o critério de aceite de qualquer serviço deve incluir "tem logs estruturados, métricas RED e traces com TraceID propagado".

Arquiteturas e padrões relacionados

SLA, SLO e SLI dependem diretamente de observabilidade: não é possível medir se um SLO de latência p99 está sendo cumprido sem métricas de histograma instrumentadas e alertas configurados.

Circuit Breaker é mais eficaz quando combinado com métricas: o estado do circuit (aberto/fechado/meio-aberto) deve ser uma métrica observável, e as decisões de abertura devem ser visíveis nos logs e traces.

API Gateway é um ponto natural de instrumentação: posicionado na borda, pode injetar TraceID em todas as requisições de entrada, coletar métricas RED de todos os serviços downstream e centralizar logs de acesso.

Filas de Mensagem adicionam complexidade ao tracing: o contexto de propagação (TraceID) precisa ser serializado nos metadados da mensagem para que o consumidor possa continuar o mesmo trace do produtor, mesmo processando a mensagem minutos depois.