Observabilidade
Observabilidade são os olhos das operações em produção — métricas revelam tendências, logs apontam causas e dados de trace identificam as causas raiz. Esses três pilares são todos indispensáveis.
1. O Que Você Vai Aprender
- Coleta de Métricas com Micrometer: Counter / Gauge / Timer / DistributionSummary
- Configuração de Scrape do Prometheus e Regras de Alerta com PromQL
- Visualizações no Dashboard Grafana: Dashboards de Métricas JVM / HTTP / Banco de Dados
- Distributed Tracing com OpenTelemetry e Propagação de Span/Context
- Bob configurou o dashboard SLO do OrderFlow: P99 < 100 ms / Taxa de Erro < 0,1% / Disponibilidade > 99,9%
2. Uma História Real de um Engenheiro SRE
(1) Dor: Troubleshooting de Problemas Online como Caixa Preta
Quando o OrderFlow encontra um erro em produção, Bob fica peneirando um mar de logs de aplicação: Qual API está lenta? Qual serviço está com problemas? Por quais microsserviços a requisição passou? Ele não tem ideia. Alice e Bob frequentemente gastam quatro horas solucionando um único problema de produção, três das quais são gastas "adivinhando" onde está o problema.
(2) Abordagens para os Três Pilares da Observabilidade
graph LR
A["Observabilidade<br/>Três Pilares"] --> B["Métricas<br/>O que aconteceu?<br/>Prometheus"]
A --> C["Logs<br/>Por que aconteceu?<br/>ELK / Loki"]
A --> D["Traces<br/>Onde aconteceu?<br/>Jaeger / Zipkin"]
Identificar problemas usando métricas, analisar logs para determinar a causa e usar análise de trace para localizar a causa raiz.
(3) Resultado
Depois que Bob estabeleceu o sistema de observabilidade, os dashboards do Grafana começaram a exibir latência P99 e taxas de erro em tempo real, os alertas do Prometheus dispararam notificações automáticas e os dados de trace do OpenTelemetry localizaram a causa raiz dos problemas em 30 segundos. O tempo médio de resolução de problemas caiu de 4 horas para 15 minutos.
3. Coleta de Dados com Micrometer
(1) Quatro Tipos de Métricas
| Tipo | Significado | Incrementa Apenas | Cenários Típicos |
|---|---|---|---|
| Counter | Contador (Apenas Incrementa) | Sim | Total de requisições, número de pedidos criados |
| Gauge | Valor Atual (Pode Aumentar ou Diminuir) | Não | Número Atual de Conexões, Comprimento da Fila |
| Timer | Distribuição de Duração | Não | Tempo de Resposta da Requisição |
| DistributionSummary | Estatísticas de Distribuição | Não | Distribuição do Tamanho do Corpo da Requisição |
(1) ▶ Exemplo: Métricas de Negócio Personalizadas
@Service
public class OrderMetrics {
private final Counter orderCreatedCounter;
private final Counter orderCancelledCounter;
private final Timer orderCreationTimer;
private final Gauge pendingOrdersGauge;
public OrderMetrics(MeterRegistry registry, OrderRepository orderRepo) {
this.orderCreatedCounter = Counter.builder("orderflow.orders.created")
.description("Total orders created")
.tag("service", "orderflow")
.register(registry);
this.orderCancelledCounter = Counter.builder("orderflow.orders.cancelled")
.description("Total orders cancelled")
.register(registry);
this.orderCreationTimer = Timer.builder("orderflow.orders.creation.duration")
.description("Order creation duration")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram()
.register(registry);
this.pendingOrdersGauge = Gauge.builder("orderflow.orders.pending",
orderRepo, repo -> repo.countByStatus("PENDING"))
.description("Current pending orders count")
.register(registry);
}
public void recordOrderCreated() {
orderCreatedCounter.increment();
}
public Timer.Sample startCreationTimer() {
return Timer.start(orderCreationTimer);
}
public void recordCreationComplete(Timer.Sample sample) {
sample.stop(orderCreationTimer);
}
}
Saída:
// Execução bem-sucedida
(2) ▶ Exemplo: Usando Métricas em um Service
@Service
public class OrderServiceImpl implements OrderService {
private final OrderMetrics metrics;
@Override
@Transactional
public Order createOrder(CreateOrderRequest request) {
Timer.Sample sample = metrics.startCreationTimer();
try {
Order order = doCreateOrder(request);
metrics.recordOrderCreated();
return order;
} finally {
metrics.recordCreationComplete(sample);
}
}
}
Saída:
// Execução bem-sucedida
4. Integração com Prometheus
(1) ▶ Exemplo: Dependências e Configuração do Prometheus
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Saída:
// Execução bem-sucedida
# application.yml
management:
endpoints:
web:
exposure:
include: health,prometheus,metrics
metrics:
tags:
application: ${spring.application.name}
export:
prometheus:
enabled: true
(2) ▶ Exemplo: Configuração de Scrape do Prometheus
# prometheus.yml
scrape_configs:
- job_name: 'orderflow'
metrics_path: '/actuator/prometheus'
scrape_interval: 15s
static_configs:
- targets: ['orderflow:8080']
Saída:
Monitoring config loaded
Prometheus targets: 3 active
Grafana dashboard: ready
(3) ▶ Exemplo: Consultas PromQL e Alertas
# Consultas PromQL
# Latência P99 para API de pedidos
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{uri=~"/api/v1/orders.*"}[5m])) by (le, uri))
# Taxa de erro (respostas 5xx)
sum(rate(http_server_requests_seconds_total{status=~"5.."}[5m]))
/
sum(rate(http_server_requests_seconds_total[5m]))
# Pedidos por minuto
rate(orderflow_orders_created_total[1m]) * 60
Saída:
Configuração aplicada com sucesso
# alert_rules.yml
groups:
- name: orderflow
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_server_requests_seconds_total{status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_total[5m])) > 0.001
for: 5m
labels:
severity: critical
annotations:
summary: "OrderFlow taxa de erro excede 0,1%"
- alert: HighP99Latency
expr: |
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le)) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "OrderFlow latência P99 excede 100ms"
5. Dashboard Grafana
(1) Métricas Chave do Kanban
| Dashboard | Métrica | PromQL |
|---|---|---|
| Taxa de Requisição | QPS | sum(rate(http_server_requests_seconds_total[5m])) |
| Latência P50/P95/P99 | Distribuição do Tempo de Resposta | histogram_quantile(0.99, ...) |
| Taxa de Erro | Proporção 5xx | rate(...{status=~"5.."})/rate(...) |
| Memória Heap JVM | Uso de Memória | jvm_memory_used_bytes{area="heap"} |
| Pausa GC | Duração do GC | rate(jvm_gc_pause_seconds_sum[5m]) |
| Conexões Ativas HikariCP | Uso do Pool de Conexões | hikaricp_connections_active |
| Taxa de Criação de Pedidos | Métrica de Negócio | rate(orderflow_orders_created_total[1m]) |
(1) ▶ Exemplo: Trecho JSON do Dashboard Grafana
{
"dashboard": {
"title": "OrderFlow Observability",
"panels": [
{
"title": "Request Rate (QPS)",
"type": "timeseries",
"targets": [{
"expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
}]
},
{
"title": "P99 Latency",
"type": "timeseries",
"targets": [{
"expr": "histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{application=\"orderflow-service\"}[5m])) by (le))"
}]
},
{
"title": "Error Rate",
"type": "gauge",
"targets": [{
"expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\",status=~\"5..\"}[5m])) / sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
}],
"fieldConfig": {
"defaults": {
"thresholds": {
"steps": [
{"value": 0, "color": "green"},
{"value": 0.001, "color": "red"}
]
}
}
}
}
]
}
}
Saída:
{
"dashboard": {
"title": "OrderFlow Observability",
"panels": [
{
"title": "Request Rate (QPS)",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
}
]
},
{
"title": "P99 Latency",
"type": "timeseries",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(http_server_request
6. Trace com OpenTelemetry
(1) O Conceito de Distributed Tracing
graph LR
A["Client"] --> B["API Gateway<br/>Trace: abc123<br/>Span 1"]
B --> C["Order Service<br/>Span 2<br/>parent: Span 1"]
C --> D["Product Service<br/>Span 3<br/>parent: Span 2"]
C --> E["Database<br/>Span 4<br/>parent: Span 2"]
| Conceito | Significado |
|---|---|
| Trace | Caminho completo de uma única requisição |
| Span | Uma operação na cadeia |
| Context | Contexto de trace passado entre spans |
| SpanId | Identificador único do span atual |
| TraceId | Identificador único do trace inteiro |
(1) ▶ Exemplo: Dependência OpenTelemetry
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
Saída:
// Execução bem-sucedida
# application.yml
otel:
exporter:
otlp:
endpoint: http://otel-collector:4317
resource:
attributes:
service.name: orderflow-service
traces:
exporter: otlp
(2) ▶ Exemplo: Span Personalizado
@Service
public class OrderService {
private final Tracer tracer;
public OrderService(Tracer tracer) {
this.tracer = tracer;
}
public Order createOrder(CreateOrderRequest request) {
Span span = tracer.spanBuilder("create-order")
.setAttribute("product.id", request.productId())
.setAttribute("quantity", request.quantity())
.startSpan();
try (Scope scope = span.makeCurrent()) {
Order order = doCreateOrder(request);
span.setAttribute("order.id", order.getId());
return order;
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
span.end();
}
}
}
Saída:
// Execução bem-sucedida
7. Exemplo Completo: Dashboard SLO do OrderFlow
# docker-compose.observability.yml
version: "3.9"
services:
prometheus:
image: prom/prometheus:latest
ports: ["9090:9090"]
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alert_rules.yml:/etc/prometheus/alert_rules.yml
grafana:
image: grafana/grafana:latest
ports: ["3000:3000"]
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana-data:/var/lib/grafana
otel-collector:
image: otel/opentelemetry-collector:latest
ports: ["4317:4317", "4318:4318"]
volumes:
- ./otel-collector-config.yml:/etc/otelcol/config.yaml
jaeger:
image: jaegertracing/all-in-one:latest
ports: ["16686:16686"]
app:
build: .
ports: ["8080:8080"]
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
OTEL_SERVICE_NAME: orderflow-service
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,prometheus,metrics
| Métrica SLO | Alvo | Limite de Alerta | PromQL |
|---|---|---|---|
| Latência P99 | < 100 ms | > 100 ms por 5 minutos | histogram_quantile(0.99, ...) |
| Taxa de Erro | < 0,1% | > 0,1% por 5 minutos | rate(5xx)/rate(all) |
| Disponibilidade | > 99,9% | < 99,9% por 5 minutos | 1 - rate(5xx)/rate(all) |
| Throughput de Pedidos | > 1.000/min | < 500/min por 10 minutos | rate(orders_created)[1m]*60 |
❓ Perguntas Frequentes
publishPercentiles e histogram_quantile?publishPercentiles calcula percentis no lado da aplicação (economizando armazenamento no Prometheus, mas menos preciso entre múltiplas instâncias). histogram_quantile calcula percentis no lado do Prometheus (suporta agregação multi-instância e é mais preciso). Recomendamos calcular no lado do Prometheus em ambientes de produção.📖 Resumo
- Quatro métricas Micrometer: Counter, Gauge (valor atual), Timer (tempo decorrido) e DistributionSummary (distribuição)
- Prometheus faz scrape de
/actuator/prometheus, consultas PromQL + regras de alerta - Dashboard Grafana exibindo métricas HTTP, JVM, pool de conexões e de negócio
- OpenTelemetry coleta dados de distributed tracing; Jaeger armazena e visualiza
- Dashboard SLO: P99 < 100 ms / Taxa de Erro < 0,1% / Disponibilidade > 99,9%
- Colaboração dos Três Pilares: Identificar problemas através de métricas → Localizar via trace → Analisar logs para determinar a causa raiz
📝 Exercícios
-
Exercício Básico (Dificuldade: ⭐): Configure Micrometer + Prometheus para o OrderFlow, exponha o endpoint
/actuator/prometheuse defina três métricas de negócio personalizadas (contagem de pedidos criados, tempo de processamento de pedidos e número de pedidos pendentes). -
Exercício Avançado (Dificuldade: ⭐⭐): Configure o Prometheus para coletar dados e configure um dashboard Grafana para exibir HTTP QPS, latência P99, taxa de erro e memória heap JVM. Escreva duas regras de alerta do Prometheus (para taxa de erro alta e latência alta).
-
Desafio (Dificuldade: ⭐⭐⭐): Integre OpenTelemetry e Jaeger para implementar distributed tracing. Adicione um span personalizado ao OrderService para rastrear todo o fluxo de realização de pedidos (Controller → Service → Repository) e visualize os detalhes do trace na UI do Jaeger.



