Construindo Observabilidade Completa com Prometheus, Grafana e Loki

Construindo Observabilidade Completa com Prometheus, Grafana e Loki
“A única coisa que sabemos com certeza é que algo vai falhar; a diferença está em saber quando e por quê.”
A observabilidade deixa de ser um luxo e passa a ser requisito básico para qualquer aplicação em produção. Neste artigo, vamos explorar como montar uma pilha de observabilidade completa usando Prometheus, Grafana, Loki e Alertmanager. Você verá conceitos, boas práticas e, principalmente, exemplos reais de configuração e código que podem ser copiados para o seu ambiente.
Sumário
Por que investir em observabilidade?
A simples monitorização – saber se um serviço está “up” ou “down” – não é suficiente nos ambientes atuais, onde a complexidade de dependências e a velocidade de entrega aumentam a probabilidade de falhas silenciosas. Observabilidade permite responder a três perguntas fundamentais:
| Pergunta | O que precisamos observar? | Por que importa |
|---|---|---|
| O que está acontecendo? | Métricas de latência, taxa de erro, uso de recursos. | Identifica degradações antes que atinjam o usuário. |
| Por que está acontecendo? | Logs estruturados que contextualizam eventos. | Facilita a raiz do problema, reduzindo MTTR. |
| O que fazer a seguir? | Alertas claros, com ações recomendadas. | Permite respostas automáticas ou manuais rápidas. |
Sem observabilidade, equipes gastam horas (ou dias) vasculhando arquivos de log dispersos, tentando reproduzir o erro. Uma pilha bem projetada reduz esse tempo para minutos.
A pilha moderna: Prometheus, Grafana, Loki e Alertmanager
| Componente | Função principal | Por que usar |
|---|---|---|
| Prometheus | Scrape de métricas em intervalos regulares, armazenamento TSDB. | Modelo de dados simples, consultas poderosas (PromQL). |
| Grafana | Visualização de métricas, logs e alertas em dashboards interativos. | Suporte nativo a Prometheus, Loki e plugins extensíveis. |
| Loki | Indexação leve de logs, busca baseada em rótulos (labels). | Integração direta com Grafana, baixo custo de armazenamento. |
| Alertmanager | Recepção, deduplicação e roteamento de alertas gerados pelo Prometheus. | Silenciamento, agrupamento e integração com canais (Slack, PagerDuty). |
A combinação desses quatro blocos cobre todo o ciclo de observabilidade: coleta → armazenamento → visualização → ação.
Métricas: coleta, modelagem e boas práticas
1. Expondo métricas na aplicação
A maioria das linguagens já oferece bibliotecas cliente. A seguir, um exemplo em Python usando a biblioteca prometheus_client:
# app_metrics.py
from prometheus_client import start_http_server, Counter, Histogram
import time
import random
Contadores
REQUEST_COUNT = Counter(
"http_requests_total",
"Número total de requisições HTTP",
["method", "endpoint", "status"]
)
Histogramas de latência
REQUEST_LATENCY = Histogram(
"http_request_duration_seconds",
"Latência das requisições HTTP",
["method", "endpoint"]
)
def handle_request(method: str, endpoint: str):
start = time.time()
# Simula processamento
time.sleep(random.uniform(0.05, 0.3))
status = random.choice([200, 200, 200, 500]) # 25% de falhas
duration = time.time() - start
REQUEST_COUNT.labels(method=method, endpoint=endpoint, status=str(status)).inc()
REQUEST_LATENCY.labels(method=method, endpoint=endpoint).observe(duration)
if __name__ == "__main__":
# Exponha as métricas na porta 8000
start_http_server(8000)
while True:
handle_request("GET", "/api/v1/items")
Dica: Mantenha os nomes das métricas curtos, em snake_case, e sempre adicione rótulos (
labels) que permitam filtrar por método, endpoint, região, etc.
2. Configurando o Prometheus para fazer o scrape
Crie o arquivo prometheus.yml:
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "python_app"
static_configs:
- targets: ["host.docker.internal:8000"]
Importante: O
scrape_intervaldeve ser balanceado entre granularidade e carga no endpoint. 15 s é um bom ponto de partida para a maioria das aplicações.
3. Modelando métricas de negócio
Além de métricas técnicas, inclua indicadores de negócio (KPIs). Por exemplo, número de pedidos concluídos:
ORDERS_COMPLETED = Counter(
"orders_completed_total",
"Total de pedidos concluídos",
["payment_method"]
)
def complete_order(payment_method: str):
# lógica de finalização...
ORDERS_COMPLETED.labels(payment_method=payment_method).inc()
Essas métricas permitem que o time de produto acompanhe a saúde do negócio diretamente no Grafana.
Logs centralizados com Loki
1. Por que Loki ao invés de ELK?
- Rótulos ao invés de full‑text indexing: reduz custos de armazenamento.
- Integração nativa com Grafana: permite criar painéis que mesclam métricas e logs.
- Escalabilidade horizontal: basta adicionar nós ao cluster.
2. Instalando Loki (Docker Compose)
# docker-compose.yml (parte Loki)
version: "3.8"
services:
loki:
image: grafana/loki:2.9.2
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9.2
volumes:
- /var/log:/var/log
- ./promtail-config.yaml:/etc/promtail/config.yaml
command: -config.file=/etc/promtail/config.yaml
Loki config (loki-config.yaml)
auth_enabled: false
server:
http_listen_port: 3100
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/cache
shared_store: filesystem
filesystem:
directory: /loki/chunks
Promtail config (promtail-config.yaml)
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*/.log
Com esses arquivos, os logs do diretório /var/log são enviados ao Loki, onde podem ser consultados por rótulos (job="varlogs").
3. Consultando logs no Grafana
No painel de Explore do Grafana, selecione a fonte Loki e use a sintaxe:
{job="varlogs", filename="/var/log/nginx/access.log"} |~ "500"
O operador |~ aplica regex, permitindo filtrar apenas respostas 5xx.
Alertas eficazes com Alertmanager
1. Definindo regras de alerta no Prometheus
Crie o arquivo alert_rules.yml:
```yaml
alert_rules.yml
groups: - name: application.rules rules: - alert: HighRequestLatency expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, endpoint)) > 0.5 for: 2m labels: severity: warning annotations: summary: "Latência alta no endpoint {{ $labels.endpoint }}" description: "A latência 95‑percentil está acima de 500 ms por mais de 2 minutos."
- alert: ErrorRateTooHigh expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.1 for: 1m labels: severity: critical annotations: summary: "Tax


