Мониторинг приложений Rust: библиотека tracing и метрики

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

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

Начать курс

Мониторинг приложений Rust: библиотека tracing и сбор метрик

Мониторинг — ключевой элемент production-систем. Для приложений на Rust, известных своей производительностью и надёжностью, важно иметь лёгкий и эффективный инструмент для сбора телеметрии. Библиотека tracing стала де-факто стандартом для observability в экосистеме Rust. Она предоставляет структурированное логирование, спаны (span) для трассировки запросов и интеграцию с системами сбора метрик.

Что такое tracing?

tracing — это фреймворк для инструментации Rust-программ. В отличие от классического логирования, он работает на основе событий (events) и спанов (spans). Спаны представляют собой временные отрезки выполнения кода (например, обработка HTTP-запроса), а события — это точки внутри спанов (например, «база данных ответила за 10 мс»).

Основные возможности:

  • Структурированное логирование — каждое событие содержит поля (ключ-значение), а не просто строку.
  • Иерархия спанов — можно вкладывать спаны друг в друга, отслеживая полный путь выполнения.
  • Асинхронная поддержка — отлично работает с tokio и async/await.
  • Subscriber (подписчик) — можно подключать различные бэкенды: stdout, OpenTelemetry, Prometheus, файлы.

Установка и базовое использование

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

[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"

Простой пример структурированного логирования:

use tracing::{info, warn, error};
use tracing_subscriber;

fn main() {
    // Инициализируем subscriber для вывода в stdout
    tracing_subscriber::fmt::init();

    let user_id = 42;
    let request_duration_ms = 150;

    info!(user_id, duration = request_duration_ms, "Запрос обработан");

    // Логгирование с уровнем warn
    warn!(user_id, "Время ответа превышает порог");

    // Логгирование с ошибкой
    error!("Не удалось подключиться к БД");
}

Вывод будет содержать поля в формате JSON-подобной строки, что упрощает парсинг в системах мониторинга.

Спаны и трассировка

Спаны позволяют измерять время выполнения операций и связывать события в цепочку:

use tracing::{span, info, Level};
use tracing_subscriber;

fn process_request(user_id: u32) {
    // Создаём span с контекстом
    let span = span!(Level::INFO, "process_request", user_id);
    let _guard = span.enter(); // активуем span

    info!("Начало обработки");
    std::thread::sleep(std::time::Duration::from_millis(50));
    info!("Обработка завершена");
}

fn main() {
    tracing_subscriber::fmt::init();
    process_request(100);
}

В выводе появится вложенный контекст, показывающий, что события относятся к span process_request.

Сбор метрик с tracing и Prometheus

Для production-мониторинга часто требуется экспорт метрик в Prometheus. Используйте крейт tracing-appender для записи в файл или tracing-opentelemetry для интеграции с OpenTelemetry. Однако самый прямой путь — использовать библиотеку metrics вместе с tracing.

Пример интеграции с metrics-exporter-prometheus:

[dependencies]
metrics = "0.21"
metrics-exporter-prometheus = "0.12"
tracing = "0.1"
tokio = { version = "1", features = ["full"] }

Код для сбора метрик:

use metrics::{counter, gauge, histogram};
use metrics_exporter_prometheus::PrometheusBuilder;
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // Запускаем HTTP-сервер для Prometheus
    let addr: SocketAddr = "0.0.0.0:9090".parse().unwrap();
    let builder = PrometheusBuilder::new();
    builder
        .with_http_listener(addr)
        .install()
        .expect("Не удалось запустить экспортёр метрик");

    // Симуляция работы
    loop {
        // Счётчик запросов
        counter!("http_requests_total", "method" => "GET", "endpoint" => "/api").increment(1);

        // Гистограмма времени ответа (в миллисекундах)
        histogram!("request_duration_ms", "endpoint" => "/api").record(120.5);

        // Gauge для текущего количества активных соединений
        gauge!("active_connections").set(10.0);

        tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;                    

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