Мониторинг приложений Rust: Обзор инструментов и библиотек

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

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

Начать курс

Мониторинг приложений Rust: Обзор ключевых инструментов и библиотек

Rust — это язык системного программирования, который всё чаще используется для создания высоконагруженных и критически важных сервисов. Однако даже самый надёжный код может давать сбои в production-среде. Эффективный мониторинг позволяет вовремя обнаружить проблемы, понять их причину и обеспечить стабильную работу приложения. В этой статье мы рассмотрим основные библиотеки и инструменты для мониторинга приложений на Rust: от сбора метрик до распределённого трейсинга.

1. Сбор и экспорт метрик

Метрики — это числовые показатели состояния приложения (количество запросов, время ответа, использование памяти и т.д.). Для их сбора в Rust используются две основные библиотеки.

1.1. metrics и metrics-exporter-prometheus

Экосистема metrics предоставляет лёгкий и гибкий способ регистрации метрик. Она поддерживает счётчики, гистограммы, датчики и другие типы. Для экспорта в Prometheus используется крейт metrics-exporter-prometheus.

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Настройка HTTP-эндпоинта для Prometheus
    let addr: SocketAddr = "0.0.0.0:9000".parse()?;
    let builder = PrometheusBuilder::new().with_http_listener(addr);
    builder.install()?;

    // Регистрация метрик
    counter!("http_requests_total", 1);
    histogram!("request_duration_seconds", 0.045);

    // Запись метрик в фоне
    loop {
        counter!("http_requests_total", 1);
        histogram!("request_duration_seconds", 0.023);
        tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
    }
}

Плюсы: высокая производительность, минимальное потребление памяти, поддержка кастомных экспортёров.

1.2. opentelemetry (метрики)

OpenTelemetry — это универсальный стандарт для сбора телеметрии. Крейт opentelemetry поддерживает метрики, трейсы и логи. Для экспорта в Prometheus используется opentelemetry-prometheus.

use opentelemetry::{global, KeyValue};
use opentelemetry::metrics::MeterProvider;
use opentelemetry_prometheus::PrometheusExporter;

fn init_meter() {
    let exporter = PrometheusExporter::default();
    let provider = opentelemetry::sdk::metrics::MeterProvider::builder()
        .with_reader(exporter)
        .build();
    global::set_meter_provider(provider);
}

fn main() {
    init_meter();
    let meter = global::meter("my_app");
    let counter = meter.u64_counter("requests").init();

    counter.add(1, &[KeyValue::new("method", "GET")]);
}

Плюсы: стандартизация, возможность переключения между бэкендами (Prometheus, Datadog, InfluxDB).

2. Распределённый трейсинг

Трейсинг помогает отслеживать путь запроса через несколько микросервисов. Для Rust лучшим выбором является tracing.

2.1. Крейт tracing и tracing-opentelemetry

Библиотека tracing предоставляет структурированное логирование и трейсинг. Она интегрируется с OpenTelemetry через tracing-opentelemetry.

use tracing::{info, span, Level};
use tracing_subscriber::fmt;
use opentelemetry::global;
use opentelemetry_otlp::WithExportConfig;

fn init_tracer() {
    let tracer = opentelemetry_otlp::new_pipeline()
        .tracing()
        .with_exporter(opentelemetry_otlp::new_exporter().tonic().with_endpoint("http://localhost:4317"))
        .install_simple()
        .expect("Failed to install OTLP tracer");
    let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
    let subscriber = fmt::Subscriber::builder()
        .with_env_filter("info")
        .finish()
        .with(telemetry);
    tracing::subscriber::set_global_default(subscriber).expect("Failed to set subscriber");
}

#[tracing::instrument]
fn process_request(user_id: u64) {
    let span = span!(Level::INFO, "handler", user_id = user_id);
    let _guard = span.enter();
    info!("Processing request");
    // ...
}

fn main() {
    init_tracer();
    process_request(42);
}

Плюсы: минимальное влияние на производительность, поддержка асинхронных контекстов, автоматическое связывание спанов.

3. Логирование

Структурированные логи —

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