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

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

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

Начать курс

Введение: Почему логирование — это не просто вывод текста?

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

1. Выбор библиотеки для логирования: spdlog и альтернативы

Стандартная библиотека C++ не предоставляет встроенных средств для гибкого логирования. Рекомендуется использовать проверенные сторонние решения. Spdlog — одна из самых популярных библиотек благодаря высокой производительности и простоте интеграции.

Установка spdlog (vcpkg или header-only)

// Установка через vcpkg
vcpkg install spdlog

// Или используйте header-only версию
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"

Базовый пример настройки логгера

#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"

int main() {
    // Создаем консольный логгер с цветным выводом
    auto console = spdlog::stdout_color_mt("console");
    
    // Устанавливаем уровень логирования
    spdlog::set_level(spdlog::level::debug);
    
    // Примеры сообщений
    console->info("Приложение запущено");
    console->warn("Обнаружена потенциальная проблема");
    console->error("Критическая ошибка: {}", errno);
    
    return 0;
}

Альтернативы: Boost.Log (для enterprise-проектов), Google Glog (если нужна интеграция с Google Test). Для встраиваемых систем используйте EasyLogger.

2. Уровни логирования и фильтрация

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

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

Настройка фильтрации в spdlog

// Включаем только WARN и выше
spdlog::set_level(spdlog::level::warn);

// Для отладки включаем все уровни
#ifdef _DEBUG
    spdlog::set_level(spdlog::level::trace);
#endif

// Пример использования с макросами
#define LOG_TRACE(...)   spdlog::trace(__VA_ARGS__)
#define LOG_ERROR(...)   spdlog::error(__VA_ARGS__)

LOG_TRACE("Вход в функцию processData()");
LOG_ERROR("Не удалось открыть файл: {}", filename);

Совет: Никогда не используйте TRACE в production-сборках — это может снизить производительность в 10-100 раз из-за форматирования строк.

3. Производительность логирования: синхронный vs асинхронный режим

В высоконагруженных системах синхронное логирование может стать узким местом. Каждый вызов логгера блокирует поток до завершения записи. Решение — асинхронное логирование.

Настройка асинхронного логгера в spdlog

#include "spdlog/async.h"
#include "spdlog/sinks/rotating_file_sink.h"

int main() {
    // Создаем асинхронный логгер с размером очереди 8192
    spdlog::init_thread_pool(8192, 1); // 1 поток для обработки
    
    auto async_file = spdlog::rotating_logger_mt<spdlog::async_factory>(
        "async_logger", "logs/app.log", 1024*1024*5, 3); // 5MB, 3 файла
    
    // Логирование теперь не блокирует вызывающий поток
    async_file->info("Быстрый лог без задержек");
    
    return 0;
}

Сравнение производительности

  • Синхронный: ~500 000 записей/сек (зависит от диска)
  • Асинхронный: ~2 000 000 записей/сек (ограничение CPU)

Важно: В асинхронном режиме существует риск потери последних сообщений при аварийном завершении. Используйте spdlog::shutdown() перед выходом.

4. Форматирование и ротация логов

Правильный формат логов упрощает их парсинг и анализ. Рекомендуемый формат: [timestamp] [level] [thread_id] [file:line] message.

Настройка пользовательского формата

// Устанавливаем глобальный формат
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] %v");

// Формат для конкретного логгера
auto logger = spdlog::basic_logger_mt("custom", "logs/custom.log");
logger->set_pattern("%v"); // Только сообщение

Ротация логов по размеру

// Максимальный размер файла 10MB, храним 5 архивов
auto rotating = spdlog::rotating_logger_mt("rotating", "logs/app.log", 
                                            10485760, 5);
// Автоматическое сжатие старых файлов (опционально)
rotating->flush_on(spdlog::level::info);

Ротация по времени

#include "spdlog/sinks/daily_file_sink.h"

// Новый файл каждый день в 00:00
auto daily = spdlog::daily_logger_mt("daily", "logs/daily.log", 
                                      0, 0); // час, минута

Рекомендации по ротации:

  • Для серверов: ротация по размеру (10-100MB) плюс удаление старых фай

Блоги

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