Логирование в Rust приложениях: лучшие практики настройки

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

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

Начать курс

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

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

1. Основы: крейты log и env_logger

Экосистема Rust предлагает модульный подход. Крейт log предоставляет абстракцию (фасад), а конкретную реализацию (бэкенд) подключает отдельный крейт, например env_logger или pretty_env_logger.

Установка и настройка

Добавьте зависимости в Cargo.toml:

[dependencies]
log = "0.4"
env_logger = "0.10"

Базовый пример

use log::{info, warn, error};

fn main() {
    env_logger::init();

    info!("Приложение запущено");
    let user_id = 42;
    warn!("Пользователь {} не найден в кэше", user_id);
    error!("Ошибка подключения к базе данных: {:?}", "timeout");
}

Важно: По умолчанию логи не выводятся, пока вы не установите переменную окружения RUST_LOG. Запустите программу так:

RUST_LOG=info cargo run

Уровни логирования

Крейт log поддерживает пять стандартных уровней (от наиболее приоритетного к наименее):

  • error — критические ошибки, требующие немедленного вмешательства.
  • warn — предупреждения о потенциальных проблемах.
  • info — общая информация о ходе выполнения.
  • debug — отладочная информация для разработчиков.
  • trace — максимально детализированные события (часто используются в циклах).

Фильтрация по модулям: RUST_LOG=my_crate=debug,other_crate=info.

2. Продвинутое логирование: крейт tracing

Для сложных приложений (веб-серверы, микросервисы) рекомендуется использовать крейт tracing. Он предоставляет концепцию span (интервалов времени) и структурированное логирование с контекстом.

Установка tracing

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

Пример с span

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

fn main() {
    tracing_subscriber::fmt::init();

    let parent_span = span!(Level::INFO, "обработка_запроса", request_id = 123);
    let _guard = parent_span.enter();
    process_data();
}

#[instrument]
fn process_data() {
    info!("Обработка данных начата");
    std::thread::sleep(std::time::Duration::from_millis(50));
    info!("Обработка данных завершена");
}

Атрибут #[instrument] автоматически создаёт span для функции и записывает время выполнения. Вывод будет содержать вложенные временные метки, что незаменимо для профилирования.

3. Структурированные логи (JSON) и ротация

В production-среде логи часто собираются централизованно (ELK, Loki, Grafana). Для этого удобно выводить логи в формате JSON.

Вывод в JSON с tracing-subscriber

use tracing_subscriber::fmt::format::Json;
use tracing_subscriber::prelude::*;

fn main() {
    let format = Json::new()
        .with_timer(tracing_subscriber::fmt::time::UtcTime::rfc3339())
        .with_target(true)
        .with_level(true);

    tracing_subscriber::fmt()
        .event_format(format)
        .init();

    tracing::info!("Сервер запущен на порту 8080");
}

Теперь логи будут выглядеть так:

{"timestamp":"2025-04-10T12:00:00Z","level":"INFO","target":"my_app","message":"Сервер запущен на порту 8080"}

Ротация логов с крейтом log4rs

Для записи в файлы с ротацией используйте log4rs:

[dependencies]
log4rs = "1.2"

Конфигурация в коде:

use log4rs::{
    append::rolling_file::{
        RollingFileAppender,
        policy::compound::{
            CompoundPolicy,
            trigger::size::SizeTrigger,
            roller::fixed_window::FixedWindowRoller,
        },
    },
    config::{Appender, Config, Root},
    encode::pattern::PatternEncoder,
};

fn main() {
    let window_roller = FixedWindowRoller::builder()
        .build("logs/app_{}.log", 5)
        .unwrap();
    let size_trigger = SizeTrigger::new(10_000_000); // 10 MB

    let policy = CompoundPolicy::new(Box::new(size_trigger), Box::new(window_roller));

    let file_appender = RollingFileAppender::builder()
        .encoder(Box::new(PatternEncoder::new("{d(%Y-%m-%d %H:%M:%S)} {l} - {m}\n")))
        .build("logs/app.log", Box::new(policy))
        .unwrap();

    let config = Config::builder()
        .appender(Appender::builder().build("file", Box::new(file_appender)))
        .build(Root::builder().appender("file").build(log::LevelFilter::Info))
        .unwrap();

    log4rs::init_config(config).unwrap();

    log::info!("Логирование с ротацией настроено");
}

Этот код создаёт файлы app.log, app_1.log... app_5.log и ротирует их при достижении 10 МБ.

4. Лучшие практики для production

Избегайте синхронного логирования в горячих путях

Запись в файл — блокирующая операция. В высоконагруженных системах используйте асинхронный бэкенд, например tracing-appender с NonBlocking.

Не логируйте секреты и чувствительные данные

Всегда очищайте пароли

Блоги

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