Как организовать сбор метрик в Go-приложении с помощью Prometheus?

Ответ

Для сбора метрик в Go-приложениях с помощью Prometheus используется официальная клиентская библиотека github.com/prometheus/client_golang. Процесс состоит из нескольких ключевых шагов:

  1. Определение и регистрация метрик: Метрики объявляются как глобальные переменные и регистрируются при старте приложения.
  2. Публикация метрик: Создается HTTP-эндпоинт (обычно /metrics), который Prometheus будет опрашивать.
  3. Изменение значений метрик: В коде приложения значения метрик обновляются при наступлении определенных событий (например, при обработке HTTP-запроса).

Пример комплексной реализации:

package main

import (
    "net/http"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

// 1. Определяем метрики
var (
    httpRequestsTotal = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "myapp_http_requests_total",
            Help: "Количество всех HTTP-запросов.",
        },
        []string{"method", "path"}, // Метки (labels) для детализации
    )

    httpRequestDuration = promauto.NewHistogram(
        prometheus.HistogramOpts{
            Name: "myapp_http_request_duration_seconds",
            Help: "Гистограмма времени ответа на HTTP-запросы.",
        },
    )
)

// Middleware для сбора метрик
func prometheusMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        duration := time.Since(start)

        // 3. Изменяем значения метрик
        httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path).Inc()
        httpRequestDuration.Observe(duration.Seconds())
    })
}

func main() {
    // Регистрация метрик происходит автоматически благодаря promauto

    // 2. Публикуем метрики на эндпоинте /metrics
    http.Handle("/metrics", promhttp.Handler())

    // Оборачиваем наш основной обработчик в middleware
    finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })
    http.Handle("/", prometheusMiddleware(finalHandler))

    http.ListenAndServe(":8080", nil)
}

Основные типы метрик:

  • Counter: Монотонно возрастающий счетчик (например, общее число запросов).
  • Gauge: Значение, которое может как увеличиваться, так и уменьшаться (например, количество активных горутин).
  • Histogram: Распределение значений по бакетам (корзинам). Позволяет вычислить квантили (например, 95-й перцентиль времени ответа) на стороне сервера Prometheus. Идеально для времени ответа или размера запроса.
  • Summary: Также измеряет распределение, но вычисляет квантили на стороне клиента. Сложнее в агрегации и более ресурсоемок для приложения.

Лучшие практики:

  • Именование: Используйте префикс с названием приложения (myapp_) для избежания конфликтов.
  • Метки (Labels): Используйте метки для добавления контекста (метод, статус код, эндпоинт), но избегайте высокой кардинальности (например, не используйте user_id в качестве метки).
  • Гистограммы для задержек: Для измерения времени ответа всегда предпочитайте Histogram, а не Summary, так как их можно корректно агрегировать в Prometheus.