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.
#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. Эти макросы позволяют лаконично логировать сообщения на разных уровнях, включая стандартное и потоковое логирование.
-
Основное логирование:
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++ — это библиотека, работающая только с заголовками. Чтобы интегрировать её в ваш проект, выполните следующие шаги:
- Клонируйте репозиторий с его подмодулями:
git clone --recurse-submodules https://github.com/NewYaroslav/log-it-cpp.git- Включите заголовочные файлы LogIt++ в ваш проект:
#include <logit.hpp>- Настройте зависимости.
При 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 использовал вложенные копии из этого репозитория.
- (Необязательно) Включите макросы fmt:
LogIt++ включает библиотеку fmt для форматирования с {}. Чтобы использовать макросы LOGIT_FMT_* и LOGIT_SCOPE_FMT_*, соберите библиотеку с опцией CMake -DLOGIT_WITH_FMT=ON.
Все параметры сборки перечислены ниже:
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 в целом».
- Сборка:
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 и отключить лишние метаданные.
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++ может отправлять сообщения в системные журналы.
Доступен при 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 работает");Работает при LOGIT_WITH_WIN_EVENT_LOG=ON в Windows. Уровни: TRACE/DEBUG/INFO → INFORMATION, WARN → WARNING, ERROR/FATAL → ERROR.
LOGIT_ADD_EVENT_LOG_DEFAULT();
LOGIT_ERROR("Что-то пошло не так");На неподдерживаемых платформах оба логгера компилируются в заглушки.
При сборке с Emscripten библиотека работает в однопоточном режиме. ConsoleLogger функционирует как обычно, а файловые логгеры заменены на заглушки, которые выводят предупреждение при использовании.
Подробную документацию для LogIt++, включая описание API и примеры использования, можно найти здесь.
Эта библиотека распространяется под лицензией MIT. Подробности смотрите в файле LICENSE в репозитории.
