Métricas com Prometheus client_golang
1. Introdução ao Prometheus e client_golang
Prometheus é um sistema de monitoramento e alerta de código aberto, parte da CNCF, que coleta métricas de sistemas alvo em intervalos regulares via HTTP. O ecossistema inclui armazenamento time-series, uma linguagem de consulta poderosa (PromQL) e integração com o Alertmanager para notificações.
O pacote client_golang é a biblioteca oficial para instrumentar aplicações Go. Com ele, você expõe métricas que o servidor Prometheus coleta. Os quatro tipos principais de métricas são:
- Counter: valor que só aumenta (requisições, erros)
- Gauge: valor que sobe e desce (memória, goroutines ativas)
- Histogram: amostra observações em buckets configuráveis (latência)
- Summary: calcula quantiles configuráveis (latência com percentis)
2. Configuração e registro de métricas
Instale o pacote com:
go get github.com/prometheus/client_golang/prometheus
Importe os pacotes necessários:
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
Você pode usar o registry global padrão (recomendado para a maioria dos casos) ou criar um registry customizado para isolamento:
// Registry global (padrão)
var (
requestsTotal = prometheus.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
})
)
func init() {
prometheus.MustRegister(requestsTotal) // panic se falhar
}
// Registry customizado
var customRegistry = prometheus.NewRegistry()
var customCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "custom_requests_total",
Help: "Custom counter.",
})
func init() {
err := customRegistry.Register(customCounter)
if err != nil {
log.Fatalf("Falha ao registrar métrica: %v", err)
}
}
Use MustRegister() para simplicidade em init() e Register() quando precisar tratar erros explicitamente.
3. Implementação dos tipos de métricas
Counter
var requestCounter = prometheus.NewCounter(prometheus.CounterOpts{
Name: "app_requests_total",
Help: "Total number of requests processed.",
})
func handleRequest() {
requestCounter.Inc() // incrementa em 1
// requestCounter.Add(5) // incrementa em 5
}
Gauge
var activeGoroutines = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "app_active_goroutines",
Help: "Current number of active goroutines.",
})
func worker() {
activeGoroutines.Inc()
defer activeGoroutines.Dec()
// processa trabalho
}
// Ou defina diretamente:
activeGoroutines.Set(float64(runtime.NumGoroutine()))
Histogram e Summary
var requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "app_request_duration_seconds",
Help: "Request latency distributions.",
Buckets: prometheus.DefBuckets, // [0.005, 0.01, 0.025, ..., 10]
})
var requestSummary = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "app_request_summary_seconds",
Help: "Request latency summary.",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
})
func handleWithTiming() {
start := time.Now()
defer func() {
duration := time.Since(start).Seconds()
requestDuration.Observe(duration)
requestSummary.Observe(duration)
}()
// processa requisição
}
4. Exposição das métricas via HTTP
Use promhttp.Handler() para expor o registry global:
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
Para registry customizado:
http.Handle("/metrics", promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{}))
Middleware para coleta automática de métricas HTTP:
var httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
},
[]string{"code", "method"},
)
func init() {
prometheus.MustRegister(httpRequestsTotal)
}
func instrumentedHandler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rec := statusRecorder{ResponseWriter: w, status: http.StatusOK}
handler.ServeHTTP(&rec, r)
httpRequestsTotal.WithLabelValues(
strconv.Itoa(rec.status),
r.Method,
).Inc()
})
}
type statusRecorder struct {
http.ResponseWriter
status int
}
func (r *statusRecorder) WriteHeader(code int) {
r.status = code
r.ResponseWriter.WriteHeader(code)
}
5. Métricas avançadas e labels
Labels permitem segmentar métricas por dimensões:
var requestByEndpoint = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_by_endpoint_total",
Help: "Requests segmented by endpoint, method and status.",
},
[]string{"endpoint", "method", "status"},
)
func trackRequest(endpoint, method string, status int) {
requestByEndpoint.With(prometheus.Labels{
"endpoint": endpoint,
"method": method,
"status": strconv.Itoa(status),
}).Inc()
}
Boas práticas com labels:
- Evite alta cardinalidade (ex.: user_id, session_id) — pode explodir séries temporais
- Prefira labels com valores limitados (ex.: método HTTP, status code)
- Use prometheus.Labels para clareza
6. Integração com coleta e alertas (visão prática)
Configure o prometheus.yml para scrape:
scrape_configs:
- job_name: 'go-app'
static_configs:
- targets: ['localhost:8080']
Exemplo de consulta PromQL:
# Taxa de requisições por segundo nos últimos 5 minutos
rate(http_requests_total[5m])
# Latência média (p50) a partir do histograma
histogram_quantile(0.5, rate(app_request_duration_seconds_bucket[5m]))
Para alertas no Alertmanager:
groups:
- name: go-app
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
7. Boas práticas e considerações finais
Evite bloqueios na coleta:
Use GaugeFunc para métricas que podem ser caras de calcular:
var memAlloc = prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "app_mem_alloc_bytes",
Help: "Current memory allocation.",
},
func() float64 {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return float64(m.Alloc)
},
)
Concorrência: client_golang é thread-safe por padrão. Não precisa de locks adicionais.
Checklist de instrumentação:
- Nomes seguem convenção: namespace_subsystem_unit_suffix (ex.: http_requests_total)
- Documente cada métrica com Help descritivo
- Teste com prometheus/testutil:
import "github.com/prometheus/client_golang/prometheus/testutil"
func TestCounter(t *testing.T) {
counter := prometheus.NewCounter(prometheus.CounterOpts{Name: "test_counter"})
counter.Inc()
if testutil.ToFloat64(counter) != 1.0 {
t.Error("Expected 1.0")
}
}
A instrumentação com client_golang é direta e poderosa. Comece com métricas básicas (Counter para requisições, Gauge para recursos) e evolua para Histograms quando precisar de análise de latência. Lembre-se: métricas são tão úteis quanto a qualidade dos alertas que geram — invista tempo em PromQL e nas regras de alerta.
Referências
- Documentação oficial do Prometheus client_golang — Referência completa da API, tipos de métricas e opções de configuração.
- Guia de instrumentação do Prometheus — Tutorial oficial para instrumentar aplicações Go com métricas e labels.
- Prometheus Best Practices on Metric and Label Naming — Convenções de nomenclatura e boas práticas para evitar explosão de cardinalidade.
- Writing Exporters for Prometheus — Guia avançado sobre criação de exporters, incluindo uso de Collectors customizados.
- Testando métricas com prometheus/testutil — Documentação do pacote de testes para validar métricas instrumentadas.
- PromQL Cheat Sheet — Resumo prático de consultas PromQL para métricas comuns como taxas, percentis e agregações.