Ответ
Для организации мониторинга в Go-приложениях я придерживаюсь подхода, основанного на обсервабилити (observability), который включает три столпа: метрики, логирование и трассировку. Для сбора метрик стандартом де-факто в cloud-native среде является Prometheus.
Да, я работал с официальной клиентской библиотекой prometheus/client_golang
. Она позволяет инструментировать приложение, то есть встраивать в код сбор метрик, и предоставлять их по HTTP-эндпоинту /metrics
для сбора сервером Prometheus.
Основные типы метрик и их применение:
- Counter: Монотонно растущий счетчик. Идеален для подсчета общего числа запросов, количества ошибок, обработанных задач. Пример:
http_requests_total
. - Gauge: Значение, которое может как увеличиваться, так и уменьшаться. Используется для измерения текущих значений. Пример:
current_active_goroutines
,cpu_temperature
. - Histogram: Измеряет распределение наблюдений по заданным корзинам (бакетам). Отлично подходит для измерения времени ответа (latency) или размера запроса. Позволяет вычислять квантили (например, 95-й или 99-й перцентиль) на стороне сервера Prometheus.
- Summary: Похож на Histogram, но вычисляет квантили на стороне клиента. Используется реже из-за большей нагрузки на приложение.
Ключевой особенностью Prometheus является использование лейблов (labels) — пар ключ-значение для разделения метрик. Это позволяет создавать многомерные данные. Например, можно отслеживать HTTP-запросы не просто как общее число, а с разбивкой по методу, пути и коду ответа: http_requests_total{method="POST", path="/api/users", code="201"}
.
Пример базовой интеграции:
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// Используем promauto для автоматической регистрации метрик
var (
httpRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "myapp_http_requests_total",
Help: "Total number of HTTP requests.",
},
[]string{"method", "path"}, // Лейблы для разделения
)
httpRequestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "myapp_http_request_duration_seconds",
Help: "Duration of HTTP requests.",
Buckets: prometheus.DefBuckets, // Стандартные бакеты
},
[]string{"path"},
)
)
// Middleware для инструментирования HTTP-хендлеров
func prometheusMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timer := prometheus.NewTimer(httpRequestDuration.WithLabelValues(r.URL.Path))
httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path).Inc()
next.ServeHTTP(w, r)
timer.ObserveDuration()
})
}
func main() {
// Оборачиваем наши хендлеры в middleware
mainMux := http.NewServeMux()
mainMux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
// Отдельный эндпоинт для метрик
http.Handle("/metrics", promhttp.Handler())
http.Handle("/", prometheusMiddleware(mainMux))
http.ListenAndServe(":8080", nil)
}
Этот подход с использованием middleware позволяет легко добавлять мониторинг ко всем HTTP-эндпоинтам, не загромождая бизнес-логику.