A plataforma
14 · Observabilidade
Toda aplicação nasce cumprindo um contrato de telemetria. A pilha que recebe os
sinais é do toolbox; a aplicação só emite, do jeito combinado. A biblioteca que
cumpre o contrato é escolha da stack.
O contrato
| Sinal | Contrato | O que não fazer |
|---|---|---|
| Logs | Contrato: JSON no stdout, um evento por linha, com @timestamp, log.level, log.logger, message, service.name, e traceId/spanId quando houver trace | O que não fazer: arquivo de log; exporter de log próprio; dado pessoal ou segredo no log |
| Métricas | Contrato: formato Prometheus numa porta interna separada, não publicada no Dockerfile nem roteada pelo proxy | O que não fazer: push de métricas; métricas na porta pública |
| Traces | Contrato: OTLP para o Alloy do toolbox; propagação W3C traceparent; requisição HTTP, banco e chamadas de saída instrumentados | O que não fazer: falar direto com o Tempo; agent externo sem ADR |
| Saúde | Contrato: GET /up → 200 trivial, sem autenticação, na porta da aplicação | O que não fazer: expor o health detalhado publicamente |
| Identificação | Contrato: service.name = nome da aplicação; deployment.environment = local | production | O que não fazer: nomes diferentes entre Kamal, métricas e traces |
| Resiliência | Contrato: coletor fora do ar não derruba requisição nem boot (export não bloqueante) | O que não fazer: — |
O que cada stack decide
| Lacuna | Exemplo (Java/Spring) |
|---|---|
{{LACUNA:obs.log}} | Exemplo (Java/Spring): log estruturado nativo do Spring Boot, formato ECS |
{{LACUNA:obs.metricas}} | Exemplo (Java/Spring): Micrometer + micrometer-registry-prometheus em /actuator/prometheus |
{{LACUNA:obs.traces}} | Exemplo (Java/Spring): Micrometer Tracing + bridge OpenTelemetry + exporter OTLP; instrumentação do pool JDBC |
{{LACUNA:obs.porta}} | Exemplo (Java/Spring): 9090 (MANAGEMENT_SERVER_PORT) |
Prova
A change observabilidade (Fase 2) entrega, por aplicação, um teste de integração do
contrato: uma requisição gera um trace com span de banco; a chamada de saída leva
traceparent; o log é JSON e tem traceId; as métricas só respondem na porta interna;
/up responde sem login; sem coletor, as requisições seguem 200.
Local
docker compose --profile observability up -d sobe um Tempo e um Grafana mínimos
(sem login). As aplicações mandam OTLP para o Tempo; sem o perfil, o export falha em
silêncio. O guia observabilidade-local.md mostra como ver um trace, as métricas e o
log de uma requisição.
Em produção
O ARCHITECTURE.md de cada stack tem uma seção "Acompanhamento no Grafana" com essas
duas listas, preenchidas para as aplicações dela.
No portão 3: o que levar ao toolbox
Três passos, do mantenedor do toolbox, com os valores do
contrato da plataforma. O
guia de implantação da stack traz estes blocos já preenchidos.
1. As métricas. Em apps/observability/prometheus/prometheus.yml, em scrape_configs, um
job_name igual ao nome de cada aplicação, porque é por ele que o painel filtra:
# <produto>
- job_name: <app>
static_configs:
- targets: ["<app>:<porta de métricas>"]O prometheus.yml é um arquivo montado pelo accessory prometheus, e o kamal deploy não o
reenvia. De dentro de apps/observability/:
kamal accessory reboot prometheus
sleep 30 # a primeira coleta
kamal accessory exec prometheus --reuse "wget -qO- --post-data query=up http://localhost:9090/api/v1/query" \
| grep -o '"job":"<app>"},"value":\[[^]]*\]'O valor "1" diz que o Prometheus alcança a aplicação. "0" é um alvo que não responde, e
nenhuma linha quer dizer que o arquivo novo não subiu.
2. Os traces. As aplicações já mandam OTLP sobre HTTP para
http://toolbox-observability-alloy:4318. No Alloy (apps/observability/alloy/config.alloy),
o receptor e a entrega ao Tempo, quando o toolbox tiver um:
otelcol.receiver.otlp "default" {
http {
endpoint = "0.0.0.0:4318"
}
output {
traces = [otelcol.processor.batch.default.input]
}
}
otelcol.processor.batch "default" {
output {
traces = [otelcol.exporter.otlp.tempo.input]
}
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = "toolbox-observability-tempo:4317"
tls {
insecure = true
}
}
}O config.alloy também é montado: depois dele, kamal accessory reboot alloy.
3. O painel. A stack versiona o JSON do painel do produto, que usa a fonte de dados de uid
prometheus, a mesma do toolbox. O mantenedor o copia para
apps/observability/grafana/dashboards/. Como ele entra na imagem do Grafana, basta o
kamal deploy do toolbox. As taxas usam janelas de 5 minutos, então o painel precisa de uns
minutos de dados.