Prometheus метрики в Rust: библиотека prometheus и metrics-exporter-prometheus

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Prometheus метрики в Rust: мониторинг производительности

Prometheus — это стандарт де-факто для сбора и анализа метрик в современных микросервисных архитектурах. В экосистеме Rust существует несколько зрелых библиотек, позволяющих легко интегрировать экспорт метрик в формате Prometheus. В этой статье мы рассмотрим две основные библиотеки: prometheus (официальный клиент) и metrics-exporter-prometheus (часть фреймворка metrics).

Зачем нужны метрики в Rust?

Мониторинг Rust-приложений критически важен для:

  • Отслеживания производительности (latency, throughput)
  • Выявления утечек памяти и проблем с ресурсами
  • Настройки алертов на основе пороговых значений
  • Анализа трендов и планирования ёмкости

Библиотека prometheus (prometheus-rs)

Официальный Rust-клиент для Prometheus. Предоставляет полный набор инструментов для создания и экспорта метрик.

Установка

Добавьте в Cargo.toml:

[dependencies]
prometheus = { version = "0.13", features = ["process"] }
hyper = { version = "0.14", features = ["http1", "server"] }

Базовый пример: HTTP-сервер с метриками

use prometheus::{Counter, HistogramOpts, HistogramVec, IntCounter, Registry, TextEncoder};
use std::net::SocketAddr;
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
use std::convert::Infallible;
use std::sync::Arc;
use tokio::sync::Mutex;

#[tokio::main]
async fn main() {
    let registry = Registry::new();

    // Создаём счётчик запросов
    let request_counter = IntCounter::new("http_requests_total", "Total HTTP requests")
        .unwrap();
    registry.register(Box::new(request_counter.clone())).unwrap();

    // Гистограмма для времени ответа
    let request_duration = HistogramVec::new(
        HistogramOpts::new("http_request_duration_seconds", "HTTP request duration"),
        &["method", "path"]
    ).unwrap();
    registry.register(Box::new(request_duration.clone())).unwrap();

    let data = Arc::new(Mutex::new((request_counter, request_duration)));

    let make_svc = make_service_fn(move |_conn| {
        let data = data.clone();
        async move {
            Ok::<_, Infallible>(service_fn(move |req: Request| {
                let data = data.clone();
                async move {
                    let start = std::time::Instant::now();
                    let (counter, duration) = &*data.lock().await;
                    counter.inc();

                    // Обработка запроса
                    let response = match req.uri().path() {
                        "/metrics" => {
                            let encoder = TextEncoder::new();
                            let metric_families = registry.gather();
                            let mut buffer = vec![];
                            encoder.encode(&metric_families, &mut buffer).unwrap();
                            Response::new(Body::from(buffer))
                        }
                        "/" => Response::new(Body::from("Hello, World!")),
                        _ => Response::builder()
                            .status(404)
                            .body(Body::from("Not Found"))
                            .unwrap(),
                    };

                    let elapsed = start.elapsed().as_secs_f64();
                    duration
                        .with_label_values(&[req.method().as_str(), req.uri().path()])
                        .observe(elapsed);

                    Ok::<_, Infallible>(response)
                }
            }))
        }
    });

    let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
    let server = Server::bind(&addr).serve(make_svc);
    println!("Listening on http://{}", addr);
    if let Err(e) = server.await {
        eprintln!("Server error: {}", e);
    }
}

Библиотека metrics-exporter-prometheus

Часть экосистемы metrics — более абстрактного и модульного подхода к метрикам. Позволяет легко переключаться между бэкендами (Prometheus, StatsD, stdout).

Установка

[dependencies]
metrics = "0.21"
metrics-exporter-prometheus = "0.12"

Пример использования

use metrics::{counter, histogram, describe_counter, describe_histogram};
use metrics_exporter_prometheus::PrometheusBuilder;
use std::net::SocketAddr;
use std::time::Instant;

fn main() {
    // Настройка экспортёра
    let builder = PrometheusBuilder::new();
    let handle = builder
        .listen_address(SocketAddr::from(([127, 0, 0, 1], 9000)))
        .install()
        .expect("Failed to install Prometheus exporter");

    // Описание метрик (опционально)
    describe_counter!("http_requests_total", "Total number of HTTP requests");
    describe_histogram!("http_request_duration_seconds", "HTTP request duration in seconds");

    // Эмуляция работы
    for i in 0..100 {
        let start = Instant::now();
        // Симуляция обработки запроса
        std::thread::sleep(std::time::Duration::from_millis(10));                    

Рекомендаций