Логирование в C++: лучшие практики для начинающих

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

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

Начать курс

Введение: Зачем нужно логирование в C++?

Логирование (журналирование) — это процесс записи событий, происходящих в программе во время её выполнения. Для начинающих C++ разработчиков может показаться, что достаточно выводить ошибки в консоль с помощью std::cout. Однако в реальных проектах грамотное логирование является критически важным инструментом для отладки, мониторинга и анализа работы приложения.

Без системы логирования вы рискуете:

  • Пропустить редкие баги, которые сложно воспроизвести
  • Затруднить диагностику проблем в продакшене
  • Создать нечитаемый код с разбросанными std::cout
  • Потерять информацию о последовательности событий при сбое

В этой статье мы разберём лучшие практики логирования в C++ для начинающих: от выбора библиотеки до настройки производительности.

Основные библиотеки для логирования в C++

Почему не стоит писать свой логгер?

Многие новички пытаются написать собственный класс для логирования. Это ошибка: готовые библиотеки уже решают проблемы производительности, потокобезопасности, ротации файлов и гибкого форматирования. Рассмотрим два популярных варианта.

1. spdlog — быстрый и современный выбор

spdlog — это header-only библиотека (не требует компиляции), которая считается одной из самых быстрых в C++. Она поддерживает асинхронное логирование, множество форматов вывода (JSON, проценты, цвета в консоли) и простую настройку.

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

#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>

int main() {
    // Создаём логгер, который пишет в файл
    auto logger = spdlog::basic_logger_mt("file_logger", "logs/app.log");
    spdlog::set_default_logger(logger);
    
    // Уровни логирования
    spdlog::info("Приложение запущено");
    spdlog::warn("Низкий уровень памяти: {}%", 15);
    spdlog::error("Ошибка подключения к базе данных");
    
    // Логирование с контекстом
    spdlog::debug("Пользователь {} выполнил действие {}", "admin", 42);
    
    return 0;
}

2. Boost.Log — для enterprise-проектов

Если вы уже используете Boost в проекте, Boost.Log — отличный выбор. Он предоставляет максимальную гибкость: фильтрацию по уровням, несколько приемников (файл, консоль, syslog), поддержку пользовательских атрибутов.

Пример настройки:

#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>

namespace logging = boost::log;

int main() {
    // Добавляем файловый приемник
    logging::add_file_log("logs/boost.log");
    
    BOOST_LOG_TRIVIAL(trace) << "Трассировочное сообщение";
    BOOST_LOG_TRIVIAL(info) << "Информационное сообщение";
    BOOST_LOG_TRIVIAL(error) << "Критическая ошибка";
    
    return 0;
}

Совет для начинающих: Начните с spdlog — он проще в установке (просто скопируйте заголовочные файлы) и имеет отличную документацию.

Уровни логирования: когда что использовать

Правильное использование уровней — основа читаемого лога. В C++ приняты следующие уровни (от наиболее подробного к критическому):

  • TRACE — максимально подробная трассировка (вход/выход из функций, значения переменных). Используйте только при отладке.
  • DEBUG — отладочная информация, которая поможет разработчику (состояние объектов, результаты промежуточных расчетов).
  • INFO — важные события работы приложения (запуск, остановка, успешная обработка запроса).
  • WARN — потенциальные проблемы, которые не нарушают работу (низкий запас памяти, устаревший API).
  • ERROR — ошибки, которые требуют вмешательства, но приложение может продолжить работу (сбой сетевого запроса).
  • CRITICAL/FATAL — фатальные ошибки, после которых приложение аварийно завершается.

Пример настройки уровней в spdlog:

#include <spdlog/spdlog.h>

int main() {
    // Устанавливаем минимальный уровень для вывода
    spdlog::set_level(spdlog::level::debug);
    
    // Эти сообщения будут выведены
    spdlog::debug("Отладка: значение x = {}", 10);
    spdlog::info("Инфо: всё хорошо");
    
    // Это сообщение не будет выведено (уровень ниже debug)
    spdlog::trace("Трассировка: шаг 1");
    
    // В продакшене обычно устанавливают info или warn
    // spdlog::set_level(spdlog::level::warn);
    
    return 0;
}

Лучшие практики логирования в C++

1. Избегайте дорогих операций в логах

Логирование не должно замедлять приложение. Никогда не вычисляйте сложные значения, если лог не будет выведен:

// ПЛОХО: функция всегда вызывается, даже при уровне info
spdlog::debug("Результат: {}", expensive_function());

// ХОРОШО: проверяем уровень перед вызовом
if (spdlog::should_log(spdlog::level::debug)) {
    spdlog::debug("Результат: {}", expensive_function());
}

// ЛУЧШЕ: используем лямбда-выражения (spdlog 1.x)
spdlog::debug("Результат: {}", [&] { return expensive_function(); });

2. Используйте асинхронное логирование

В высоконагруженных приложениях синхронная запись в файл может стать узким местом. Асинхронное логирование передаёт запись в отдельный поток:

#include <spdlog/async.h>
#include <spdlog/sinks/basic_file_sink.h>

int main() {
    // Создаём асинхронный логгер
    spdlog::init_thread_pool(8192, 1); // размер очереди, кол-во потоков
    auto logger = spdlog::basic_logger_mt("async_logger", "logs/async.log");
    spdlog::set_default_logger(logger);
    
    // Теперь запись в фай                

Блоги

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