Skip to content

Latest commit

 

History

History
888 lines (644 loc) · 60.8 KB

File metadata and controls

888 lines (644 loc) · 60.8 KB

LogIt++ Library

LogIt++ Logo

Обзор

LogIt++ — макро-ориентированная библиотека логирования на C++ с поддержкой компиляторов начиная с C++11. Она сочетает лёгкие макросы инструментирования с настраиваемыми бэкендами (консоль, вращающиеся файлы, syslog, Windows Event Log или пользовательские приёмники) и направляет сообщения через асинхронную очередь, чтобы приложения оставались отзывчивыми даже при подробной диагностике. Библиотека объединяет удобство макросов, знакомое по IceCream-Cpp, и гибкость решений вроде spdlog.

Ключевые особенности:

  • Макро-ориентированный API. Единые семейства макросов (LOGIT_<LEVEL>, LOGIT_PRINTF_<LEVEL>, LOGIT_STREAM_<LEVEL> и др.) покрывают мгновенные сообщения, форматирование в стиле printf, потоковый вывод, ограничения частоты и работу с тегами. Определите LOGIT_SHORT_NAME перед подключением <logit.hpp>, чтобы включить компактные алиасы LOG_I, LOG_WPF, LOG_S_INFO и другие.
  • Гибкое форматирование и маршрутизация. Настраивайте шаблоны формата, комбинируйте консольные/файловые/системные бэкенды или подключайте собственные реализации логгеров.
  • Асинхронность по умолчанию. Каждый бэкенд обслуживается исполнителем задач с настраиваемыми размерами очереди и политиками переполнения, а макросы вроде LOGIT_WARN_ONCE или LOGIT_ERROR_THROTTLE помогают упорядочить повторяющиеся сообщения.

Обычное использование библиотеки строится вокруг публичных семейств макросов LOGIT_* / LOG_*. Выбирайте семейство под нужный стиль логирования: обычный вызов, printf, потоковый вывод, условие, таргетирование или scope-замер.

Структура заголовков

Библиотека включает несколько самодостаточных «зонтичных» заголовков, которые подготавливают зависимости в правильном порядке:

Точка входа Назначение
<logit.hpp> Подключает конфигурацию, перечисления, утилиты, форматтеры, все логгеры, синглтон Logger и публичные макросы.
<logit/utils.hpp> Собирает модуль logit/utils/, включая LogRecord, утилиты форматирования и работы с аргументами.
<logit/formatter.hpp> Предоставляет интерфейсы форматтеров и стандартный компилятор паттернов.
<logit/loggers.hpp> Экспортирует готовые бэкенды логгеров и подготавливает их внутренние зависимости.

Обычному прикладному коду достаточно <logit.hpp>. Если вы работаете напрямую с конкретным типом модуля и подключаете листовой заголовок явно, соблюдайте правило NHR (Nearest Header Requirement): сначала подключите соответствующий зонтик, затем конкретный файл, например для прямого использования MemoryLogger:

#include <logit/loggers.hpp>
#include <logit/loggers/MemoryLogger.hpp>

Внутренние файлы из logit/detail/ предназначены только для реализации и не должны подключаться напрямую пользователями.

Ниже приведены примеры макросов; также загляните в каталог examples/ с отдельными сценариями, включая настройку очереди и обработку аварийного завершения.

Примеры макросов

Полные макросы

#include <logit.hpp>

int main() {
    LOGIT_ADD_CONSOLE_DEFAULT();
    LOGIT_SET_MAX_QUEUE(32);
    LOGIT_SET_QUEUE_POLICY(LOGIT_QUEUE_DROP);

    const bool verbose = true;
    int attempt = 1;
    double latency_ms = 12.5;

    LOGIT_TRACE0();
    LOGIT_DEBUG_IF(verbose, "Подробная диагностика включена");
    LOGIT_INFO("Запуск сервиса", attempt);
    LOGIT_WARN_ONCE("инициализация подсистемы");
    LOGIT_ERROR_EVERY_N(3, "повторное подключение", attempt);
    LOGIT_ERROR_THROTTLE(250, "сбой не устранён");
    LOGIT_PRINTF_WARN("Задержка %.2f ms", latency_ms);
    LOGIT_FORMAT_INFO("%.2f", 1.23f, 4.56f);
    LOGIT_INFO_TAG(({{"order_id", 123}, {"side", "BUY"}}), "отправлен ордер");
    LOGIT_SECTION("Proxy");
    LOGIT_RAW("Proxy enabled: False");
    LOGIT_STREAM_INFO() << "Потоковый вывод: " << attempt;

    LOGIT_WAIT();
}

Короткие алиасы

Определите LOGIT_SHORT_NAME перед подключением <logit.hpp>, чтобы использовать односимвольные префиксы уровней:

#define LOGIT_SHORT_NAME
#include <logit.hpp>

void short_names_demo() {
    LOGIT_ADD_CONSOLE_DEFAULT(); // вызывайте один раз при инициализации

    int attempt = 2;

    LOG_I("Короткий алиас для INFO");
    LOG_IPF("Попытка %d завершена", attempt);
    LOG_W("Предупреждение");
    LOG_WPF("Повтор %d/3", attempt);
    LOG_S_INFO() << "Потоковый алиас " << attempt;
}

Самодостаточный пример, который объединяет настройки и намеренно завершает работу после fatal-сообщения, расположен в examples/example_logit_minimal_crash.cpp.

Макросы системных ошибок

LOGIT_SYSERR_<LEVEL> захватывает текущее значение errno (или GetLastError() в Windows) и добавляет расшифровку ошибки к исходному сообщению, чтобы в логе оставался как контекст, так и код сбоя. При необходимости можно явно выбрать платформу через LOGIT_PERROR_<LEVEL> или LOGIT_WINERR_<LEVEL>.

#include <logit.hpp>
#include <fcntl.h>

int main() {
    LOGIT_ADD_CONSOLE_DEFAULT();

    if (::open("missing.cfg", O_RDONLY) == -1) {
        LOGIT_SYSERR_ERROR("Не удалось открыть конфигурацию");
    }

    LOGIT_WAIT();
}

Формат суффикса можно изменить на этапе компиляции с помощью макросов из config.hpp:

#define LOGIT_OS_ERROR_JOIN " <- "
#define LOGIT_POSIX_ERROR_PATTERN "[%s] errno=%d (%s)"
#define LOGIT_WINDOWS_ERROR_PATTERN "[%s] GetLastError=%lu (%s)"
#include <logit.hpp>

// ... далее в коде ...
LOGIT_SYSERR_ERROR("Ошибка удаления временного каталога");

Таргетированные, условные и scope-макросы

Помимо базовых вызовов, библиотека предоставляет канонические семейства для адресного логирования в конкретный бэкенд, условного вывода и измерения длительности scope.

#include <logit.hpp>

void process_request(bool verbose, double latency_ms) {
    LOGIT_ADD_CONSOLE_DEFAULT();
    LOGIT_ADD_UNIQUE_FILE_LOGGER_DEFAULT_SINGLE_MODE();

    LOGIT_INFO_TO(1, "Сообщение только для логгера с индексом 1");
    LOGIT_PRINTF_INFO_IF(verbose, "Latency %.2f ms", latency_ms);

    LOGIT_SCOPE_INFO("process_request");
    LOGIT_SCOPE_PRINTF_WARN_T(50, "slow path latency=%.2f ms", latency_ms);

    LOGIT_WAIT();
}

Буфер последних логов в памяти

Для удалённого управления, диагностических endpoint-ов и операторских инструментов можно зарегистрировать отдельный in-memory backend и получать снимок последних логов по индексу логгера.

#include <logit.hpp>

int main() {
    LOGIT_ADD_MEMORY_LOGGER_SINGLE_MODE(1000, 1024 * 1024, 24LL * 60 * 60 * 1000); // индекс 0

    LOGIT_INFO_TO(0, "remote-ready info");
    LOGIT_WARN_TO(0, "latest warning");

    const auto lines = LOGIT_GET_BUFFERED_STRINGS(0);
    const auto entries = LOGIT_GET_BUFFERED_ENTRIES(0);
    const auto level = LOGIT_GET_LOG_LEVEL(0);

    (void)lines;
    (void)entries;
    (void)level;
}

MemoryLogger возвращает снимки в хронологическом порядке: от старых записей к новым. Параметр max_bytes ограничивает суммарный объём именно отформатированных текстов сообщений, а не полный memory footprint каждого BufferedLogEntry. Snapshot-чтение не берёт глобальный exec_mx из Logger, но по-прежнему синхронизируется на собственном mutex самого memory-backend-а, пока копирует текущий буфер.

Файловые backend-ы также поддерживают доступ к уже сохранённым логам через LOGIT_LIST_LOG_FILES(index), LOGIT_READ_LOG_FILE(index, path) и LOGIT_READ_LOG_FILES(index, paths). Эти helpers читают только то, что уже успело попасть на диск, не дренируют асинхронные очереди и пока рассматривают сжатые rotated-файлы как metadata-only записи. Используйте MemoryLogger для почти real-time снимков, а файловые API — для операционного чтения логов за сегодня или предыдущие дни.

Обратное давление и горячее изменение размера

Асинхронный TaskExecutor поддерживает как очередь на основе std::deque под мьютексом, так и опциональный lock-free MPSC ring (включается флагом LOGIT_USE_MPSC_RING). Политики переполнения (Block, DropNewest, DropOldest) ведут себя одинаково в обеих конфигурациях; в MPSC-режиме DropOldest намеренно отбрасывает входящую задачу, чтобы не нарушать порядок уже приня тых. Кольцевой буфер по умолчанию вмещает LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY задач (1024) и может быть перенастроен ком бинацией LOGIT_SET_MAX_QUEUE(...) с этим макросом, если приложению требуется другой базовый объём. В сборках с MPSC допускаетс я "горячее" изменение размера очереди без потери принятых задач — продюсеры кратковременно ждут, пока поток-воркер пересобирает буфер.

Возможности

  • Гибкое форматирование логов:

Настраивайте формат сообщений логов с помощью паттернов. Вы можете переопределить паттерны через макросы или указать их напрямую при добавлении бэкенда логгера. Поддерживаются как стандартные флаги форматирования (%H, %M, %S, %v и другие), так и специальные, такие как %N([...]) для указания fallback-логов без аргументов.

#define LOGIT_CONSOLE_PATTERN "%H:%M:%S.%e | %^%N([%!g:%#])%v%$"

try {
    throw std::runtime_error("An example runtime error");
} catch (const std::exception& ex) {
    LOGIT_FATAL(ex);
}

// Вывод:
> 23:59:59.128 | An example runtime error
  • Логирование с использованием макросов:

Легко логируйте переменные и сообщения с помощью макросов. Выберите макрос в зависимости от стиля форматирования:

  • LOGIT_PRINTF_<LEVEL> повторяет поведение функции printf и позволяет задавать формат для каждого аргумента.
  • LOGIT_FORMAT_<LEVEL> применяет один и тот же формат ко всему списку аргументов.
float someFloat = 123.456f;
int someInt = 789;
LOGIT_INFO(someFloat, someInt);

auto now = std::chrono::system_clock::now();
LOGIT_PRINT_INFO("TimePoint example: ", now);
LOGIT_PRINTF_INFO("%.2f %d", someFloat, someInt); // как printf
LOGIT_FORMAT_INFO("%.2f", someFloat, 654.321f);  // один формат для всех аргументов
  • Фильтры и ограничение частоты логов:

Сократите шум от повторяющихся сообщений с помощью макросов LOGIT_WARN_ONCE, LOGIT_INFO_EVERY_N и LOGIT_ERROR_THROTTLE. Макросы с суффиксом _THROTTLE (например, LOGIT_INFO_THROTTLE) ограничивают вывод одним сообщением за заданный промежуток времени.

for (int i = 0; i < 10; ++i) {
    LOGIT_WARN_ONCE("initializing");                    // выводится один раз
    LOGIT_INFO_EVERY_N(3, "heartbeat", i);              // каждое 3-е сообщение
    LOGIT_ERROR_THROTTLE(200, "repeated error");        // не чаще 1 раза/200 мс
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
  • Поддержка нескольких бэкендов:

Легко настройте логгеры для вывода в консоль и файлы. При необходимости добавьте отправку сообщений на сервер или в базу данных, создавая собственные бэкенды.

// Добавление трёх бэкендов: консольного, файлового и уникального файлового логгера
LOGIT_ADD_CONSOLE_DEFAULT();
LOGIT_ADD_FILE_LOGGER_DEFAULT();
LOGIT_ADD_UNIQUE_FILE_LOGGER_DEFAULT_SINGLE_MODE();
  • Системные бэкенды:

Использование системных журналов: SyslogLogger для POSIX syslog и EventLogLogger для Windows Event Log.

  • Асинхронное логирование:

Улучшите производительность приложения с помощью асинхронного логирования. Все логгеры по умолчанию обрабатывают сообщения в отдельном потоке.

  • Потоковое логирование:

Используйте операторы потоков для сложных сообщений.

LOGIT_STREAM_INFO() << "Stream-based info logging with short macro. Integer value: " << 123;
  • Расширяемость:

Создавайте собственные логгеры и форматтеры для удовлетворения ваших специфических потребностей.

class CustomLogger : public logit::ILogger {
public:
    CustomLogger() = default;

    /// \brief Логирует сообщение, форматируя запись и сообщение.
    /// \param record Лог-запись с деталями события.
    /// \param message Отформатированное сообщение лога.
    void log(const logit::LogRecord& record, const std::string& message) override {
        // Реализация отправки логов...
    }

    ~CustomLogger() override = default;
};

LOGIT_ADD_LOGGER(CustomLogger, (), logit::SimpleLogFormatter, ("%v"));

Справочник макросов

Шаблон макроса Описание
LOGIT_<LEVEL>(...) Логирование с указанным уровнем (TRACE, DEBUG, INFO, WARN, ERROR, FATAL).
LOGIT_PRINT_<LEVEL>(...) Логирование заранее сформированной строки или сообщения, собранного через потоки.
LOGIT_PRINTF_<LEVEL>(fmt, ...) Форматирование в стиле printf с плейсхолдерами для каждого аргумента.
LOGIT_FORMAT_<LEVEL>(fmt, ...) Применение одного и того же формата ко всем аргументам.
LOGIT_FMT_<LEVEL>(fmt, ...) Форматирование через fmt, если включён LOGIT_WITH_FMT.
LOGIT_STREAM_<LEVEL>() Потоковое логирование через <<; короткие версии LOG_S_<LEVEL>() доступны при определении LOGIT_SHORT_NAME.
LOGIT_<LEVEL>_IF(condition, ...) Логирование только если условие истинно.
LOGIT_PRINT_<LEVEL>_IF(...), LOGIT_PRINTF_<LEVEL>_IF(...), LOGIT_FORMAT_<LEVEL>_IF(...), LOGIT_FMT_<LEVEL>_IF(...) Условные варианты для семейств print/printf/format/fmt.
LOGIT_<LEVEL>_ONCE(...) Логирование только при первом вызове.
LOGIT_<LEVEL>_EVERY_N(n, ...) Логирование каждого n-го вызова.
LOGIT_<LEVEL>_THROTTLE(period_ms, ...) Логирование не чаще одного раза за period_ms миллисекунд.
LOGIT_<LEVEL>_TAG(({{"k", "v"}}), msg) Добавление к сообщению пар ключ-значение.
LOGIT_RAW(msg), LOGIT_RAW_TO(index, msg), LOGIT_RAW_IF(condition, msg) Вывод готового текста без фильтрации по уровню и без применения formatter-паттерна.
LOGIT_SECTION(name), LOGIT_SECTION_TO(index, name), LOGIT_SECTION_IF(condition, name) Вывод raw-секций вида [Proxy].
LOGIT_<LEVEL>_TO(index, ...) Логирование только в логгер с указанным индексом, включая single-mode бэкенды.
LOGIT_PRINT_<LEVEL>_TO(...), LOGIT_PRINTF_<LEVEL>_TO(...), LOGIT_FORMAT_<LEVEL>_TO(...), LOGIT_FMT_<LEVEL>_TO(...), LOGIT_STREAM_<LEVEL>_TO(...) Таргетированные варианты для print/printf/format/fmt/stream.
LOGIT_SCOPE_<LEVEL>(phase) / LOGIT_SCOPE_<LEVEL>_T(threshold_ms, phase) RAII-макросы для логирования длительности scope, опционально только при превышении порога.
LOGIT_SCOPE_PRINTF_<LEVEL>(...) / LOGIT_SCOPE_PRINTF_<LEVEL>_T(...) Scope-таймеры с форматированием в стиле printf.
LOGIT_SCOPE_FMT_<LEVEL>(...) / LOGIT_SCOPE_FMT_<LEVEL>_T(...) Scope-таймеры с форматированием через fmt.
LOGIT_PERROR_<LEVEL>(msg), LOGIT_WINERR_<LEVEL>(msg), LOGIT_SYSERR_<LEVEL>(msg) Добавляют к сообщению расшифрованную платформенную ошибку.
LOGIT_ADD_LOGGER(...) и backend-макросы семейства LOGIT_ADD_* Регистрируют консольные, memory, файловые, unique-file, crash, syslog, event-log и пользовательские бэкенды.
LOGIT_GET_*, LOGIT_SET_*, LOGIT_IS_*, LOGIT_WAIT(), LOGIT_SHUTDOWN() Управление состоянием логгеров и исполнителя задач.
LOGIT_QUEUE_*, LOGIT_GET_DROPPED_TASKS(), LOGIT_RESET_DROPPED_TASKS() Константы политики очереди и счётчики отброшенных задач.

Согласованные алиасы для коротких имён и совместимости тоже существуют, но в таблице выше перечислены канонические публичные семейства.

Макросы конфигурации

Макрос Описание
LOGIT_BASE_PATH Базовый путь, который обрезается из __FILE__ в сообщениях.
LOGIT_DEFAULT_COLOR Цвет сообщений по умолчанию.
LOGIT_COLOR_TRACE, LOGIT_COLOR_DEBUG, LOGIT_COLOR_INFO, LOGIT_COLOR_WARN, LOGIT_COLOR_ERROR, LOGIT_COLOR_FATAL, LOGIT_COLOR_DEFAULT Переопределяют цвета по уровням и цвет по умолчанию для консоли.
LOGIT_WALLCLOCK_MS() / LOGIT_MONOTONIC_MS() Позволяют заменить источники wall-clock и monotonic времени, используемые библиотекой.
LOGIT_CURRENT_TIMESTAMP_MS() Переопределяет основной хук получения timestamp для лог-записей.
LOGIT_CONSOLE_PATTERN Паттерн форматирования вывода в консоль по умолчанию.
LOGIT_FILE_LOGGER_PATH Каталог для файловых логов.
LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS Срок хранения файловых логов перед автоочисткой.
LOGIT_FILE_LOGGER_PATTERN Паттерн форматирования файлового логгера по умолчанию.
LOGIT_FILE_LOGGER_MAX_FILE_SIZE_BYTES Порог ротации по размеру для файлового логгера.
LOGIT_FILE_LOGGER_MAX_ROTATED_FILES Максимальное количество сохранённых ротированных файлов.
LOGIT_UNIQUE_FILE_LOGGER_PATH Каталог для логов по одному сообщению в файл.
LOGIT_UNIQUE_FILE_LOGGER_PATTERN Паттерн unique-file логгера по умолчанию.
LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH Длина хеша в именах unique-file логов.
LOGIT_OS_ERROR_JOIN, LOGIT_POSIX_ERROR_PATTERN, LOGIT_WINDOWS_ERROR_PATTERN, LOGIT_SYSTEM_ERROR_PATTERN Управляют тем, как к сообщению добавляется расшифровка системной ошибки.
LOGIT_TAGS_JOIN, LOGIT_TAG_PAIR_SEP, LOGIT_TAG_KV_SEP, LOGIT_TAG_QUOTE_VALUES Управляют отображением key-value тегов после сообщения.
LOGIT_TASK_EXECUTOR_BLOCK_WAIT_USEC Частота ожидания для блокирующих продюсеров при переполнении очереди.
LOGIT_TASK_EXECUTOR_DRAIN_BUDGET Сколько задач worker может вычитать за одну итерацию в ring-режиме.
LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY Ёмкость MPSC-ring по умолчанию для очереди без лимита.
LOGIT_SHORT_NAME Включает компактные алиасы вроде LOG_I, LOG_WPF, LOG_S_INFO.

Функциональные макросы

Макрос Описание
LOGIT_SET_MAX_QUEUE(size) Устанавливает размер очереди задач (0 — без ограничений).
LOGIT_SET_QUEUE_POLICY(mode) Поведение при переполнении: LOGIT_QUEUE_DROP_NEWEST, LOGIT_QUEUE_DROP_OLDEST или LOGIT_QUEUE_BLOCK.
LOGIT_SET_LOG_LEVEL_TO(index, level) Задает минимальный уровень для конкретного логгера.
LOGIT_SET_LOG_LEVEL(level) Задает минимальный уровень для всех логгеров.
LOGIT_GET_LOG_LEVEL(index) Читает текущий минимальный уровень логирования конкретного логгера.
LOGIT_SET_LOGGER_ENABLED(index, enabled) Включает или отключает логгер.
LOGIT_IS_LOGGER_ENABLED(index) Проверяет, включен ли логгер.
LOGIT_SET_SINGLE_MODE(index, single_mode) Включает режим «один файл — одно сообщение».
LOGIT_IS_SINGLE_MODE(index) Проверяет, активен ли режим «один файл — одно сообщение».
LOGIT_SET_TIME_OFFSET(index, offset_ms) Сдвигает временную метку логгера.
LOGIT_GET_STRING_PARAM(index, param) Получает строковый параметр логгера.
LOGIT_GET_INT_PARAM(index, param) Получает целочисленный параметр логгера.
LOGIT_GET_FLOAT_PARAM(index, param) Получает параметр с плавающей точкой.
LOGIT_GET_LAST_FILE_NAME(index) Имя последнего файла, в который писал логгер.
LOGIT_GET_LAST_FILE_PATH(index) Путь к последнему файлу логгера.
LOGIT_GET_LAST_LOG_TIMESTAMP(index) Метка времени последнего лога.
LOGIT_GET_TIME_SINCE_LAST_LOG(index) Время с последнего лога (в секундах).
LOGIT_GET_BUFFERED_STRINGS(index) Возвращает буферизованные готовые строки из логгера, который поддерживает snapshots.
LOGIT_GET_BUFFERED_ENTRIES(index) Возвращает буферизованные структурированные записи из логгера, который поддерживает snapshots.
LOGIT_WAIT() Ожидает завершения всех асинхронных логгеров.
LOGIT_SHUTDOWN() Завершает работу системы логирования.

Использование

Ниже приведен простой пример использования LogIt++ в вашем приложении. Размер очереди и поведение при переполнении настраиваются с помощью LOGIT_SET_MAX_QUEUE и LOGIT_SET_QUEUE_POLICY (используйте LOGIT_QUEUE_DROP или LOGIT_QUEUE_BLOCK):

#define LOGIT_SHORT_NAME
#include <logit.hpp>

int main() {
    LOGIT_ADD_CONSOLE_DEFAULT();
    LOGIT_SET_MAX_QUEUE(64);
    LOGIT_SET_QUEUE_POLICY(LOGIT_QUEUE_DROP);

    float a = 123.456f;
    int b = 789;
    int c = 899;
    const char* someStr = "Hello, World!";

    // Базовое логирование с использованием макросов
    LOG_INFO("Starting the application");
    LOG_DEBUG("Variable values", a, b);
    LOG_WARN("This is a warning message");

    // Логирование с форматированием
    LOG_PRINTF_INFO("Formatted log: value of a = %.2f", a);
    LOG_FORMAT_WARN("%.4d", b, c);

    // Логирование ошибок и фатальных ошибок
    LOG_ERROR("An error occurred", b);
    LOG_FATAL("Fatal error. Terminating application.");

    // Условное логирование
    LOG_ERROR_IF(b < 0, "Value of b is negative");
    LOG_WARN_IF(a > 100, "Value of a exceeds 100");

    // Потоковое логирование с использованием коротких и длинных макросов
    LOG_S_INFO() << "Logging a float: " << a << ", and an int: " << b;
    LOG_S_ERROR() << "Error occurred in the system";
    LOGIT_STREAM_WARN() << "Warning: potential issue detected with value: " << someStr;

    // Использование LOGIT_TRACE для трассировки выполнения функций
    LOGIT_TRACE0();  // Trace without arguments
    LOG_PRINT_TRACE("Entering main function with variable a =", a);

    // Ожидание завершения всех асинхронных операций логирования
    LOGIT_WAIT();

    return 0;
}

Для получения дополнительных примеров использования обратитесь к папке examples в репозитории, где можно найти подробные демонстрации различных сценариев логирования и конфигураций.

Уровень логирования на этапе компиляции

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

g++ -DLOGIT_COMPILED_LEVEL=logit::LogLevel::LOG_LVL_WARN ...

В этом примере макросы TRACE, DEBUG и INFO будут отключены на этапе компиляции.

Runtime-смена уровня через LOGIT_SET_LOG_LEVEL(...) и LOGIT_SET_LOG_LEVEL_TO(...) продолжает работать для уровней, которые попали в бинарник, но не может вернуть логи, вырезанные LOGIT_COMPILED_LEVEL.


Настройка форматов логов

LogIt++ поддерживает настраиваемое форматирование сообщений с использованием паттернов, которые определяют вид каждого сообщения. Вы можете задавать паттерны через макросы или передавать их при добавлении бэкендов логгера.

Пример формата

Пример задания пользовательского формата для консольного логгера:

LOGIT_ADD_LOGGER(
    logit::ConsoleLogger, (), 
    logit::SimpleLogFormatter, 
    ("%Y-%m-%d %H:%M:%S.%e [%l] %^%N(%g:%#)%v%$")
);

Или задайте паттерн с использованием макросов:

#define LOGIT_CONSOLE_PATTERN "%H:%M:%S.%e | %^%N([%!g:%#])%v%$"
LOGIT_ADD_CONSOLE_DEFAULT();

Логгер автоматически подставит в шаблон указанные данные, например:

23:59:59.128 | path/to/file.cpp:123 A sample log message

Флаги форматирования сообщений

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

Ниже приведен список поддерживаемых флагов форматирования:

  • Флаги форматирования даты и времени:

    • %Y: Год (например, 2024)
    • %m: Месяц (01-12)
    • %d: День месяца (01-31)
    • %H: Час (00-23)
    • %M: Минута (00-59)
    • %S: Секунда (00-59)
    • %e: Миллисекунда (000-999)
    • %C: Двузначный год (например, 24 для 2024 года)
    • %c: Полная дата и время (например, Пн Окт 4 12:45:30 2024)
    • %D: Короткая дата (например, 04/10/24)
    • %T, %X: Время в формате ISO 8601 (например, 12:45:30)
    • %F: Дата в формате ISO 8601 (например, 2024-10-04)
    • %s, %E: Unix-временная метка в секундах
    • %ms: Unix-временная метка в миллисекундах
    • %b: Сокращенное название месяца (например, Янв)
    • %B: Полное название месяца (например, Январь)
    • %a: Сокращенное название дня недели (например, Пн)
    • %A: Полное название дня недели (например, Понедельник)
  • Флаги уровня логирования:

    • %l: Полный уровень лога (например, INFO, ERROR)
    • %L: Сокращенный уровень лога (например, I для INFO, E для ERROR)
  • Флаги для файла и функции:

    • %f, %fn, %bs: Имя исходного файла
    • %g, %ffn: Полный путь к файлу
    • %#: Номер строки
    • %!: Имя функции
  • Флаги потоков:

    • %t: Идентификатор потока
  • Флаги цвета:

    • %^: Начало цветового форматирования
    • %$: Конец цветового форматирования
    • %SC: Начало удаления цветовых кодов (Strip Color)
    • %EC: Конец удаления цветовых кодов (End Color)
  • Флаги сообщения:

    • %v: Содержимое сообщения лога
    • %N(...): Используется как замена, если нет аргументов (например, при вызове LOG_TRACE0()). В скобках указывается шаблон для вывода. Пример: %N(%g:%#) добавит имя файла и номер строки, если нет сообщения.

Поддержка выравнивания и обрезки текста

LogIt++ позволяет форматировать текст сообщения с помощью ширины (width), выравнивания и обрезки:

  • Выравнивание:

    • Левое: Используется знак - перед числом ширины, например: %-10v.
    • Центрирование: Используется знак = перед числом ширины, например: %=10v.
    • Правое (по умолчанию): %10v.
  • Обрезка:

    • Символ ! после числа ширины указывает, что текст нужно обрезать, если он превышает заданную длину. Пример: %10!v.

Примеры:

  • %10v – правое выравнивание сообщения на 10 символов.
  • %-10v – левое выравнивание сообщения на 10 символов.
  • %10!v – обрезка текста до 10 символов с правым выравниванием.
  • %-10!v – левое выравнивание сообщения с обрезкой до 10 символов.

Продвинутая обработка путей

Для флагов, связанных с файлами (%f, %g, %@), при обрезке строки обеспечивается сохранение имени файла и начала пути, а средняя часть заменяется на ..., если ширина меньше длины пути.

Пример:

  • Вход: /very/long/path/to/file.cpp
  • Обрезано до ширины 15: /very...file.cpp

Укороченные макросы для логирования

LogIt++ предоставляет укороченные версии макросов для логирования, когда определён флаг LOGIT_SHORT_NAME. Эти макросы позволяют лаконично логировать сообщения на разных уровнях, включая стандартное и потоковое логирование.

Доступные макросы для уровня TRACE:

  • Основное логирование:

    • LOG_T(...): Регистрирует сообщение уровня TRACE.
    • LOG_T0(): Регистрирует сообщение уровня TRACE без аргументов.
    • LOG_0T(): Псевдоним для LOG_T0().
    • LOG_0_T(): Псевдоним для LOG_T0().
    • LOG_T_NOARGS(): Псевдоним для LOG_T0().
    • LOG_NOARGS_T(): Псевдоним для LOG_T0().
  • Логирование с форматированием:

    • LOG_TF(fmt, ...): Регистрирует отформатированное сообщение уровня TRACE с использованием строк формата.
    • LOG_FT(fmt, ...)``: Псевдоним для LOG_TF(fmt, ...)`.
    • LOG_T_PRINT(...): Регистрирует сообщение уровня TRACE, печатая каждый аргумент.
    • LOG_PRINT_T(...): Псевдоним для LOG_T_PRINT(...).
    • LOG_T_PRINTF(fmt, ...): Регистрирует отформатированное сообщение уровня TRACE с использованием форматирования в стиле printf.
    • LOG_PRINTF_T(fmt, ...): Псевдоним для LOG_T_PRINTF(fmt, ...).
    • LOG_TP(...): Псевдоним для LOG_T_PRINT(...).
    • LOG_PT(...): Псевдоним для LOG_T_PRINT(...).
    • LOG_TPF(fmt, ...): Псевдоним для LOG_T_PRINTF(fmt, ...).
    • LOG_PFT(fmt, ...): Псевдоним для LOG_T_PRINTF(fmt, ...).
  • Альтернативные макросы уровня TRACE:

    • LOG_TRACE(...): Регистрирует сообщение уровня TRACE (то же, что и LOG_T(...)).
    • LOG_TRACE0(): Регистрирует сообщение уровня TRACE без аргументов (то же, что и LOG_T0()).
    • LOG_0TRACE(): Псевдоним для LOG_TRACE0().
    • LOG_0_TRACE(): Псевдоним для LOG_TRACE0().
    • LOG_TRACE_NOARGS(): Регистрирует сообщение уровня TRACE без аргументов (то же, что и LOG_T_NOARGS()).
    • LOG_NOARGS_TRACE(): Псевдоним для LOG_TRACE_NOARGS().
    • LOG_TRACEF(fmt, ...): Регистрирует отформатированное сообщение уровня TRACE (то же, что и LOG_TF(fmt, ...)).
    • LOG_FTRACE(fmt, ...): Псевдоним для LOG_TRACEF(fmt, ...).
    • LOG_TRACE_PRINT(...): Регистрирует сообщение уровня TRACE, печатая каждый аргумент (то же, что и LOG_T_PRINT(...)).
    • LOG_PRINT_TRACE(...): Псевдоним для LOG_TRACE_PRINT(...).
    • LOG_TRACE_PRINTF(fmt, ...): Регистрирует отформатированное сообщение уровня TRACE с использованием форматирования в стиле printf (так же, как LOG_T_PRINTF(fmt, ...)).
    • LOG_PRINTF_TRACE(fmt, ...): Псевдоним для LOG_TRACE_PRINTF(fmt, ...).

Эти макросы обеспечивают гибкость и удобство при регистрации сообщений на уровне TRACE. Они позволяют выбирать между различными стилями регистрации, такими как стандартная регистрация, форматированная регистрация и печать каждого аргумента отдельно.

Примечание: Аналогичные макросы доступны для других уровней журнала — INFO (LOG_I, LOG_INFO), DEBUG (LOG_D, LOG_DEBUG), WARN (LOG_W, LOG_WARN), ERROR (LOG_E, LOG_ERROR) и FATAL (LOG_F, LOG_FATAL). Соглашения об именовании одинаковы для всех уровней, вам нужно только заменить букву уровня или слово в имени макроса.

  • Пример:
LOG_T("Trace message using short macro");
LOG_TF("%.4d", 999);
LOG_T_PRINT("Printing trace message with multiple variables: ", var1, var2);
LOG_TRACE("Trace message (alias for LOG_T)");
LOG_TRACE_PRINTF("Formatted trace: value = %d", value);

Конфигурационные макросы

LogIt++ предоставляет несколько макросов, которые позволяют настраивать библиотеку под ваши нужды. Ниже приведены доступные конфигурационные макросы:

  • LOGIT_BASE_PATH: Определяет базовый путь, используемый для файлов логов. Если LOGIT_BASE_PATH не определен или пуст ({}), будет использоваться полный путь из переменной __FILE__. Вы можете переопределить это, указав собственный базовый путь для логов.
#define LOGIT_BASE_PATH "/path/to/your/project"
  • LOGIT_DEFAULT_COLOR: Определяет цвет консоли по умолчанию. Если LOGIT_DEFAULT_COLOR не определен, по умолчанию используется TextColor::LightGray. Вы можете установить собственный цвет текста для консоли, переопределив этот макрос.
#define LOGIT_DEFAULT_COLOR TextColor::Green
  • LOGIT_CURRENT_TIMESTAMP_MS: Макрос для получения текущей временной метки в миллисекундах. По умолчанию используется std::chrono для получения метки времени. Вы можете переопределить этот макрос, чтобы использовать собственную функцию получения времени.
#define LOGIT_CURRENT_TIMESTAMP_MS() my_custom_timestamp_function()
  • LOGIT_CONSOLE_PATTERN: Определяет шаблон лога для вывода в консоль. Этот шаблон контролирует формат сообщений, отправляемых в консоль, включая временную метку, сообщение и цвет. Если LOGIT_CONSOLE_PATTERN не определен, он по умолчанию установлен на %H:%M:%S.%e | %^%v%$.
#define LOGIT_CONSOLE_PATTERN "%H:%M:%S.%e | %^%v%$"
  • LOGIT_FILE_LOGGER_PATH: Определяет путь к каталогу для логов. Если LOGIT_FILE_LOGGER_PATH не определен, по умолчанию используется "data/logs". Вы можете задать этот путь для управления тем, где будут храниться логи.
#define LOGIT_FILE_LOGGER_PATH "/custom/log/directory"
  • LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS: Определяет количество дней, по истечении которых старые файлы логов будут удалены. Если LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS не определен, по умолчанию используется 30 дней. Вы можете задать это значение для управления политикой хранения логов.
#define LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS 60  // Хранить логи 60 дней
  • LOGIT_FILE_LOGGER_PATTERN: Определяет шаблон лога для файловых логгеров. Этот шаблон контролирует формат сообщений, записываемых в файлы логов, включая временную метку, имя файла, номер строки, имя функции и информацию о потоке. Если LOGIT_FILE_LOGGER_PATTERN не определен, используется по умолчанию [%Y-%m-%d %H:%M:%S.%e] [%ffn:%#] [%!] [thread:%t] [%l] %SC%v.
#define LOGIT_FILE_LOGGER_PATTERN "[%Y-%m-%d %H:%M:%S.%e] [%l] %SC%v"
  • LOGIT_UNIQUE_FILE_LOGGER_PATH: Определяет путь по умолчанию для уникальных файлов логов. Если LOGIT_UNIQUE_FILE_LOGGER_PATH не определен, используется "data/logs/unique_logs". Вы можете указать собственный путь для уникальных файлов логов.
#define LOGIT_UNIQUE_FILE_LOGGER_PATH "/custom/unique/log/directory"
  • LOGIT_UNIQUE_FILE_LOGGER_PATTERN: Определяет шаблон лога для уникальных файлов логов. Если LOGIT_UNIQUE_FILE_LOGGER_PATTERN не определен, по умолчанию используется "%v". Вы можете настроить этот шаблон для управления форматом сообщений в уникальных файлах.
#define LOGIT_UNIQUE_FILE_LOGGER_PATTERN "%v"
  • LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH: Определяет длину хеша, используемого в уникальных именах файлов логов. Если LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH не определен, по умолчанию используется 8 символов. Это обеспечивает уникальные имена файлов для каждого лога.
#define LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH 12	 // Set hash length to 12 characters
  • LOGIT_SHORT_NAME: Включает короткие имена для макросов логирования, таких как LOG_T, LOG_D, LOG_E и другие, для более лаконичных записей логов.

Пример пользовательского логгера и форматтера

Для обычного прикладного логирования продолжайте использовать публичные макросы LOGIT_* / LOG_*. Низкоуровневый API в этом разделе нужен для точек расширения: собственных ILogger / ILogFormatter, регистрации бэкендов, тестов и другого инфраструктурного кода, который намеренно работает ниже макро-слоя.

Вы можете расширить возможности LogIt++ с помощью реализации собственных логгеров и форматтеров. Вот как это сделать:

Пример пользовательского логгера

#include <fstream>
#include <mutex>
#include <logit.hpp>

class FileLogger : public logit::ILogger {
public:
	FileLogger(const std::string& file_name) : m_file_name(file_name) {
		m_log_file.open(file_name, std::ios::out | std::ios::app);
	}

	~FileLogger() {
		if (m_log_file.is_open()) {
			m_log_file.close();
		}
	}

	void log(const logit::LogRecord& record, const std::string& message) override {
		std::lock_guard<std::mutex> lock(m_mutex);
		if (m_log_file.is_open()) {
			m_log_file << message << std::endl;
		}
	}

	void wait() override {}

private:
	std::string m_file_name;
	std::ofstream m_log_file;
	std::mutex m_mutex;
};

Пример пользовательского форматтера

#include <logit.hpp>
#include <json/json.h>

class JsonLogFormatter : public logit::ILogFormatter {
public:
	std::string format(const logit::LogRecord& record) const override {
		Json::Value log_entry;
		log_entry["level"] = static_cast<int>(record.log_level);
		log_entry["timestamp_ms"] = record.timestamp_ms;
		log_entry["file"] = record.file;
		log_entry["line"] = record.line;
		log_entry["function"] = record.function;
		log_entry["message"] = record.format;

		Json::StreamWriterBuilder writer;
		return Json::writeString(writer, log_entry);
	}
};

Установка

LogIt++ — это библиотека, работающая только с заголовками. Чтобы интегрировать её в ваш проект, выполните следующие шаги:

  1. Клонируйте репозиторий с его подмодулями:
git clone --recurse-submodules https://github.com/NewYaroslav/log-it-cpp.git
  1. Включите заголовочные файлы LogIt++ в ваш проект:
#include <logit.hpp>
  1. Настройте зависимости.

При CMake-сборке LogIt++ сначала ищет обязательный пакет TimeShield, а затем использует вложенный подмодуль external/time-shield-cpp, если он есть в этом репозитории. Если вы размещаете зависимости внутри своего проекта, используйте свои install- или vendor-пути; каталог не обязан называться external.

Опциональные зависимости нужны только для включённых возможностей: fmt для LOGIT_WITH_FMT, zlib для LOGIT_WITH_GZIP и zstd для LOGIT_WITH_ZSTD. Их можно установить как пакеты, подключить из собственной структуры зависимостей или включить LOGIT_USE_SUBMODULES=ON, чтобы CMake использовал вложенные копии из этого репозитория.

  1. (Необязательно) Включите макросы fmt:

LogIt++ включает библиотеку fmt для форматирования с {}. Чтобы использовать макросы LOGIT_FMT_* и LOGIT_SCOPE_FMT_*, соберите библиотеку с опцией CMake -DLOGIT_WITH_FMT=ON.

Опции CMake

Все параметры сборки перечислены ниже:

  • LOGIT_CPP_BUILD_TESTS (по умолчанию: ON, если проект собирается на верхнем уровне) — сборка тестов.
  • LOGIT_CPP_BUILD_EXAMPLES (по умолчанию: OFF) — сборка примеров.
  • LOGIT_BENCH_ENABLE (по умолчанию: OFF) — сборка бенчмарков; LOGIT_BENCH_WITH_SPDLOG (по умолчанию: OFF) добавляет сравнение со spdlog.
  • LOGIT_WITH_GZIP / LOGIT_WITH_ZSTD (по умолчанию: OFF) — поддержка gzip или zstd для ротируемых файлов.
  • LOGIT_WITH_FMT (по умолчанию: OFF) — подключить макросы в стиле {}; LOGIT_USE_SUBMODULES (по умолчанию: OFF) разрешает использовать вложенные опциональные зависимости, такие как fmt, zlib и zstd, при отсутствии системных пакетов.
  • LOGIT_WITH_SYSLOG (по умолчанию: ON на Unix-подобных системах) — сборка бэкенда syslog.
  • LOGIT_WITH_WIN_EVENT_LOG (по умолчанию: ON в Windows) — сборка бэкенда Windows Event Log.
  • LOGIT_FORCE_ASYNC_OFF (по умолчанию: OFF) — принудительно отключить асинхронное выполнение даже в многопоточных сборках.
  • LOGIT_USE_MPSC_RING (по умолчанию: ON) — использовать lock-free очередь вместо варианта на std::deque.
  • LOGIT_ENABLE_DROP_OLDEST_SLOWPATH (по умолчанию: ON) — скомпилировать медленный путь для DropOldest, когда кольцо заполнено.
  • LOGIT_EMSCRIPTEN (по умолчанию: ON при сборке Emscripten) — подстройка под однопоточные среды WebAssembly.

Бенчмарки

Запустите ./build/bench/logit_bench, чтобы получить полный набор измерений (sync/async × null/file × количество продюсеров × размер сообщений). Результаты дописываются в bench/results/latency.csv по одной строке на каждую библиотеку/комбинацию. При необходимости сократите нагрузку с помощью переменных окружения LOGIT_BENCH_TOTAL и LOGIT_BENCH_WARMUP.

Что на самом деле измеряет бенчмарк

Харнесс меряет end-to-end латентность (вызов лога → доставка в sink) и суммарную пропускную. Он полезен для поиска регрессий и сравнения дизайна пайплайнов, но это не идеальное соревнование «кто быстрее». LogIt++ осознанно тратит больше работы в духе Python icecream: один LOGIT_* может парсить имена аргументов, собирать args_array из VariableValue и опционально форматировать структуру. Классические printf-логгеры вроде spdlog оптимизируются под быстрое форматирование строк и очереди, без этой «леденцовой» ветки. Для корректного сравнения держите оба лагеря в одном режиме:

В этой методике LogIt++ проходит путь «record → formatter → sink/queue» с IceCream-подобными метаданными (имена/значения аргументов), а spdlog в адаптере получает уже готовую строку и измеряет «string → queue → sink».

  • Только текст / passthrough показывает стоимость dispatch/очереди/sink и ближе всего к поведению spdlog по умолчанию.
  • IceCream-стиль метаданных (LOGIT_* с захватом аргументов) включает парсинг имён и упаковку значений; тут LogIt++ делает больше работы на вызов намеренно.

Асинхронные цифры включают enqueue + пробуждение воркера/планирование ОС + работу sink; для file sink добавляется разброс из-за буферов/flush. Латентности в async сильно зависят от размера thread_pool/overflow policy и поведения sink; числа ниже отражают именно адаптер из этого репозитория, а не «spdlog в целом».

Последний снимок (05.12.2025)

  • Сборка: Release, LOGIT_BENCH_ENABLE=ON, LOGIT_BENCH_WITH_SPDLOG=ON, LOGIT_USE_MPSC_RING=ON (по умолчанию).
  • Нагрузка: LOGIT_BENCH_TOTAL=10000, 4 продюсера, размер сообщений 200 байт для таблицы сравнения (остальные комбинации см. в bench/results/latency-2025-12-05-10k.csv).
  • Метрики: медианная задержка (p50) в наносекундах и достигнутая пропускная способность (сообщений/с).
  • Железо: 3 vCPU (Intel Xeon E5-2673 v4 @ 2.30GHz) в виртуальной машине, одна NUMA-нода.
  • Данные: обновлено по bench/results/latency-2025-12-05-10k.csv (05.12.2025, 03:18 UTC).
  • Таблица отражает только этот сценарий; полный набор — в CSV.
Режим Приёмник LogIt++ p50 Пропускная (LogIt++) spdlog p50 Пропускная (spdlog)
Sync Null 119 нс 2 127 704 сооб./с 86 нс 5 803 783 сооб./с
Sync File 130 нс 1 035 690 сооб./с 87 нс 1 593 987 сооб./с
Async Null 20 916 нс 1 846 272 сооб./с 1 248 779 нс 1 303 573 сооб./с
Async File 255 323 нс 651 384 сооб./с 5 001 140 нс 1 153 976 сооб./с

Выводы: В синхронных режимах LogIt++ показывает p50 ~120–130 нс при IceCream-подобном пути метаданных; адаптер spdlog работает с готовой строкой, поэтому на Null/File быстрее в этой методике. В async обе стороны меряют enqueue + пробуждения + sink и чувствительны к конфигурации thread_pool/overflow/sink: здесь LogIt++ остаётся в десятках–сотнях микросекунд, а spdlog-адаптер уходит в миллисекунды и требует отдельной настройки/профиля для других конфигураций. При необходимости можно включить passthrough/fmt_only и отключить лишние метаданные.

Как устроен бенч-харнесс (LatencyRecorder)

  • bench/LatencyRecorder.hpp заранее резервирует слоты и ведёт Token {slot, t0_ns, active}Summary {p50, p99, p999} с защитой от повторных complete() на один слот. Доступны методы recorded(), wait_for_all() и finalize() для end-to-end измерений между продюсерами и консюмером.
  • Адаптер LogIt кладёт номер слота в LogRecord::line (см. bench/adapters/LogItAdapter.cpp). Приёмник вызывает LatencyRecorder::complete_slot(), когда видит неотрицательный номер строки; никаких дополнительных полей в записи не требуется.

Системные бэкенды

LogIt++ может отправлять сообщения в системные журналы.

Syslog (Unix)

Доступен при LOGIT_WITH_SYSLOG=ON на Unix-подобных системах. Соответствие уровней: TRACE/DEBUG → LOG_DEBUG, INFO → LOG_INFO, WARN → LOG_WARNING, ERROR/FATAL → LOG_ERR/LOG_CRIT.

LOGIT_ADD_SYSLOG_DEFAULT();
LOGIT_INFO("Syslog работает");

Windows Event Log

Работает при LOGIT_WITH_WIN_EVENT_LOG=ON в Windows. Уровни: TRACE/DEBUG/INFO → INFORMATION, WARN → WARNING, ERROR/FATAL → ERROR.

LOGIT_ADD_EVENT_LOG_DEFAULT();
LOGIT_ERROR("Что-то пошло не так");

На неподдерживаемых платформах оба логгера компилируются в заглушки.

Emscripten

При сборке с Emscripten библиотека работает в однопоточном режиме. ConsoleLogger функционирует как обычно, а файловые логгеры заменены на заглушки, которые выводят предупреждение при использовании.


Документация

Подробную документацию для LogIt++, включая описание API и примеры использования, можно найти здесь.


Лицензия

Эта библиотека распространяется под лицензией MIT. Подробности смотрите в файле LICENSE в репозитории.