diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index f8c17bb..a0791c1 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -15,25 +15,35 @@ jobs: with: submodules: "true" fetch-depth: 0 - - name: Inject version into mainpage.dox + - name: Inject development version into mainpage.dox run: | - TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "0.0.0-untagged") - TAG=$(echo "$TAG" | sed 's/^v//') - echo "Using version: $TAG" + VERSION=$(sed -n 's/^project(log-it-cpp VERSION \([^ ]*\).*/\1/p' CMakeLists.txt | head -n 1) + if [ -z "$VERSION" ]; then + if VERSION=$(git describe --tags --abbrev=0 2>/dev/null); then + VERSION=$(printf '%s' "$VERSION" | sed 's/^v//') + else + VERSION="0.0.0-untagged" + fi + fi + DOC_VERSION="${VERSION}-dev" + echo "Using version: $DOC_VERSION" test -f docs/mainpage.dox || { echo "mainpage.dox not found!"; exit 1; } - sed -i "0,/VERSION_PLACEHOLDER/s//$TAG/" docs/mainpage.dox + sed -i "0,/VERSION_PLACEHOLDER/s//${DOC_VERSION}/" docs/mainpage.dox - name: Generate Documentation - uses: mattnotmitt/doxygen-action@edge - - name: Check for documentation changes - id: docs_changed + uses: mattnotmitt/doxygen-action@v1.12.0 + - name: Validate generated documentation run: | - if git diff --quiet -- docs/html; then - echo "changed=false" >> "$GITHUB_OUTPUT" - else - echo "changed=true" >> "$GITHUB_OUTPUT" - fi + test -s docs/html/index.html + test -s docs/html/pages.html + test -s docs/html/classes.html + grep -q "1.0.2-dev" docs/html/index.html + grep -R -q "MemoryLogger" docs/html + grep -R -q "MdbxLogger" docs/html + grep -R -q "PrometheusHttpServerLogger" docs/html + grep -R -q "OtlpHttpLogger" docs/html + ! grep -R -q "AGENTS.md" docs/html + ! grep -R -q "NewYaroslav" docs/html - name: Publish generated content to GitHub Pages - if: steps.docs_changed.outputs.changed == 'true' uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.ACCESS_TOKEN }} diff --git a/Doxyfile b/Doxyfile index c8c1fc0..993e019 100644 --- a/Doxyfile +++ b/Doxyfile @@ -54,7 +54,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = "Header-only, macro-first C++ logging library" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -907,7 +907,7 @@ WARN_IF_UNDOC_ENUM_VAL = NO # Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. -WARN_AS_ERROR = NO +WARN_AS_ERROR = FAIL_ON_WARNINGS # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which @@ -952,7 +952,11 @@ WARN_LOGFILE = INPUT = ./include \ ./examples \ ./docs/mainpage.dox \ - ./docs/groups.dox + ./docs/groups.dox \ + ./docs/OtlpHttpLogger.md \ + ./docs/PrometheusLogger.md \ + ./docs/TaskExecutor.md \ + ./docs/backpressure.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1073,7 +1077,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = */AGENTS.md # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -1275,15 +1279,12 @@ VERBATIM_HEADERS = YES # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. -CLANG_ASSISTED_PARSING = NO - # If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS # tag is set to YES then doxygen will add the directory of each input to the # include path. # The default value is: YES. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. -CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that @@ -1291,7 +1292,6 @@ CLANG_ADD_INC_PATHS = YES # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. -CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This @@ -1304,7 +1304,6 @@ CLANG_OPTIONS = # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. -CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index @@ -2002,7 +2001,7 @@ EXTRA_SEARCH_MAPPINGS = # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = YES +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -2870,7 +2869,6 @@ MAX_DOT_GRAPH_DEPTH = 0 # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated diff --git a/README-RU.md b/README-RU.md index 8845f28..6488eb6 100644 --- a/README-RU.md +++ b/README-RU.md @@ -3,13 +3,24 @@ ## Обзор -**LogIt++** — макро-ориентированная библиотека логирования на C++ с поддержкой компиляторов начиная с `C++11`. Она сочетает лёгкие макросы инструментирования с настраиваемыми бэкендами (консоль, вращающиеся файлы, syslog, Windows Event Log или пользовательские приёмники) и направляет сообщения через асинхронную очередь, чтобы приложения оставались отзывчивыми даже при подробной диагностике. Библиотека объединяет удобство макросов, знакомое по **IceCream-Cpp**, и гибкость решений вроде **spdlog**. +**LogIt++** — макро-ориентированная библиотека логирования на C++. Ядро и +большинство встроенных бэкендов поддерживают `C++11`; интеграции OTLP, +Prometheus HTTP server и MDBX требуют `C++17`. Библиотека сочетает лёгкие +макросы инструментирования с настраиваемыми бэкендами (консоль, файлы, +память, системные/crash-логгеры, OTLP, Prometheus, MDBX и пользовательские +приёмники). Большинство обычных native-бэкендов асинхронны по умолчанию, но +специализированные бэкенды могут быть синхронными или иметь собственную +очередь и worker. Ключевые особенности: - **Макро-ориентированный API.** Единые семейства макросов (`LOGIT_`, `LOGIT_PRINTF_`, `LOGIT_STREAM_` и др.) покрывают мгновенные сообщения, форматирование в стиле `printf`, потоковый вывод, ограничения частоты и работу с тегами. Определите `LOGIT_SHORT_NAME` перед подключением ``, чтобы включить компактные алиасы `LOG_I`, `LOG_WPF`, `LOG_S_INFO` и другие. -- **Гибкое форматирование и маршрутизация.** Настраивайте шаблоны формата, комбинируйте консольные/файловые/системные бэкенды или подключайте собственные реализации логгеров. -- **Асинхронность по умолчанию.** Каждый бэкенд обслуживается исполнителем задач с настраиваемыми размерами очереди и политиками переполнения, а макросы вроде `LOGIT_WARN_ONCE` или `LOGIT_ERROR_THROTTLE` помогают упорядочить повторяющиеся сообщения. +- **Гибкое форматирование и маршрутизация.** Настраивайте шаблоны формата, + комбинируйте консольные, файловые, системные, telemetry- и storage-бэкенды + или подключайте собственные реализации логгеров. +- **Настраиваемая доставка.** Обычные native-бэкенды используют асинхронные + очереди по умолчанию; размер очереди, политика переполнения, dedicated + executor и синхронный режим настраиваются для поддерживаемых бэкендов. Обычное использование библиотеки строится вокруг публичных семейств макросов `LOGIT_*` / `LOG_*`. Выбирайте семейство под нужный стиль логирования: @@ -38,6 +49,13 @@ scope-замер. Ниже приведены примеры макросов; также загляните в каталог `examples/` с отдельными сценариями, включая настройку очереди и обработку аварийного завершения. +Дополнительные руководства: + +- [`docs/OtlpHttpLogger.md`](docs/OtlpHttpLogger.md) — OTLP/HTTP, callback-экспорт, атрибуты, retries, разбиение payload и сжатие. +- [`docs/PrometheusLogger.md`](docs/PrometheusLogger.md) — payload/server-бэкенды, registry, scrape и ограничения. +- [`docs/TaskExecutor.md`](docs/TaskExecutor.md) — варианты очереди, политики переполнения, hot resize и lifecycle. +- [`docs/backpressure.md`](docs/backpressure.md) — настройка очереди и счётчики отброшенных задач. + ## Примеры макросов ### Полные макросы @@ -94,6 +112,20 @@ void short_names_demo() { Самодостаточный пример, который объединяет настройки и намеренно завершает работу после fatal-сообщения, расположен в `examples/example_logit_minimal_crash.cpp`. +### Диагностический контекст (MDC/NDC) + +При `LOGIT_WITH_CONTEXT=ON` можно хранить thread-local пары MDC и вложенный +стек NDC. Эти значения доступны в форматтере через `%K`, `%K{key}` и `%J`. + +```cpp +LOGIT_MDC_PUT("request_id", "req-42"); +{ + LOGIT_NDC_GUARD("checkout"); + LOGIT_INFO("обработка заказа"); +} +LOGIT_MDC_CLEAR(); +``` + ### Макросы системных ошибок `LOGIT_SYSERR_` захватывает текущее значение `errno` (или `GetLastError()` в Windows) и добавляет расшифровку ошибки к исходному сообщению, чтобы в логе оставался как контекст, так и код сбоя. При необходимости можно явно выбрать платформу через `LOGIT_PERROR_` или `LOGIT_WINERR_`. @@ -188,6 +220,21 @@ int main() { почти real-time снимков, а файловые API — для операционного чтения логов за сегодня или предыдущие дни. +### Структурированные и telemetry-бэкенды + +Опциональный `LOGIT_WITH_MDBX` сохраняет структурированные записи и большие +payload в MDBX через `mdbx-containers`. `LOGIT_WITH_OTLP` включает OTLP-экспортёры: +`OtlpHttpLogger` отправляет HTTP через kurlyk, а `OtlpPayloadLogger` передаёт +сериализованные payload через callback. `LOGIT_WITH_PROMETHEUS` предоставляет callback с +Prometheus text payload, а `LOGIT_WITH_PROMETHEUS_SERVER` — встроенный endpoint +`/metrics`. Подробные настройки и ограничения install-сценария описаны в +английских руководствах `docs/`. + +`ConsoleLogger::Config::routes` позволяет направлять диапазоны уровней в +`std::cout`, `std::cerr` или пользовательский поток. Макросы +`LOGIT_CLEAR_LOGGER` и `LOGIT_CLEAR_ALL_LOGGERS` очищают поддерживаемые +буферы/хранилища и возвращают `LogClearResult`. + ## Обратное давление и горячее изменение размера Асинхронный `TaskExecutor` поддерживает как очередь на основе `std::deque` под мьютексом, так и опциональный lock-free MPSC ring @@ -268,7 +315,10 @@ LOGIT_ADD_UNIQUE_FILE_LOGGER_DEFAULT_SINGLE_MODE(); - **Асинхронное логирование**: -Улучшите производительность приложения с помощью асинхронного логирования. Все логгеры по умолчанию обрабатывают сообщения в отдельном потоке. +Большинство обычных native-бэкендов по умолчанию работают асинхронно. +Crash- и payload callback-бэкенды синхронны, OTLP использует собственную +очередь экспортёра, dedicated executor создаёт worker для выбранного бэкенда, +а Emscripten без pthreads работает кооперативно без OS-потока. - **Потоковое логирование**: @@ -280,25 +330,10 @@ LOGIT_STREAM_INFO() << "Stream-based info logging with short macro. Integer valu - **Расширяемость**: -Создавайте собственные логгеры и форматтеры для удовлетворения ваших специфических потребностей. - -``` -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")); -``` +Создавайте собственные логгеры и форматтеры для специфических требований. +Полная реализация, соответствующая текущим интерфейсам, приведена в разделе +[«Пользовательский логгер и форматтер»](#пример-пользовательского-логгера-и-форматтера) +ниже. ## Справочник макросов @@ -323,6 +358,7 @@ LOGIT_ADD_LOGGER(CustomLogger, (), logit::SimpleLogFormatter, ("%v")); | `LOGIT_SCOPE_(phase)` / `LOGIT_SCOPE__T(threshold_ms, phase)` | RAII-макросы для логирования длительности scope, опционально только при превышении порога. | | `LOGIT_SCOPE_PRINTF_(...)` / `LOGIT_SCOPE_PRINTF__T(...)` | Scope-таймеры с форматированием в стиле `printf`. | | `LOGIT_SCOPE_FMT_(...)` / `LOGIT_SCOPE_FMT__T(...)` | Scope-таймеры с форматированием через `fmt`. | +| `LOGIT_CLEAR_LOGGER(index)` / `LOGIT_CLEAR_ALL_LOGGERS()` | Очищают поддерживаемые записи/буферы и возвращают `LogClearResult`; варианты `_EX` принимают `LogClearOptions`. | | `LOGIT_PERROR_(msg)`, `LOGIT_WINERR_(msg)`, `LOGIT_SYSERR_(msg)` | Добавляют к сообщению расшифрованную платформенную ошибку. | | `LOGIT_ADD_LOGGER(...)` и backend-макросы семейства `LOGIT_ADD_*` | Регистрируют консольные, memory, файловые, unique-file, crash, syslog, event-log и пользовательские бэкенды. | | `LOGIT_GET_*`, `LOGIT_SET_*`, `LOGIT_IS_*`, `LOGIT_WAIT()`, `LOGIT_SHUTDOWN()` | Управление состоянием логгеров и исполнителя задач. | @@ -439,10 +475,12 @@ int main() { ### Уровень логирования на этапе компиляции -Можно исключить сообщения низких уровней из итогового бинарного файла, указав максимальный уровень для компиляции. Задайте макрос `LOGIT_COMPILED_LEVEL` при компиляции: +Можно исключить сообщения низких уровней из итогового бинарного файла, +задав минимальный уровень, включаемый в компиляцию. Задайте макрос +`LOGIT_COMPILED_LEVEL` при компиляции: ```bash -g++ -DLOGIT_COMPILED_LEVEL=logit::LogLevel::LOG_LVL_WARN ... +g++ -DLOGIT_COMPILED_LEVEL=LOGIT_LEVEL_WARN ... ``` В этом примере макросы `TRACE`, `DEBUG` и `INFO` будут отключены на этапе компиляции. @@ -734,10 +772,29 @@ public: void wait() override {} + std::string get_string_param(const logit::LoggerParam& param) const override { + (void)param; + return std::string(); + } + + int64_t get_int_param(const logit::LoggerParam& param) const override { + (void)param; + return 0; + } + + double get_float_param(const logit::LoggerParam& param) const override { + (void)param; + return 0.0; + } + + void set_log_level(logit::LogLevel level) override { m_log_level = level; } + logit::LogLevel get_log_level() const override { return m_log_level; } + private: std::string m_file_name; std::ofstream m_log_file; std::mutex m_mutex; + logit::LogLevel m_log_level = logit::LogLevel::LOG_LVL_TRACE; }; ``` @@ -749,6 +806,10 @@ private: class JsonLogFormatter : public logit::ILogFormatter { public: + void set_timestamp_offset(int64_t offset_ms) override { + (void)offset_ms; + } + std::string format(const logit::LogRecord& record) const override { Json::Value log_entry; log_entry["level"] = static_cast(record.log_level); @@ -768,7 +829,9 @@ public: ## Установка -LogIt++ — это библиотека, работающая только с заголовками. Чтобы интегрировать её в ваш проект, выполните следующие шаги: +Сам LogIt++ является header-only библиотекой, но CMake-target может добавить +транзитивные compile/link-зависимости для включённых опциональных функций. +Чтобы интегрировать библиотеку в проект, выполните следующие шаги: 1. Клонируйте репозиторий с его подмодулями: @@ -785,7 +848,40 @@ git clone --recurse-submodules https://github.com/LimiNode/log-it-cpp.git При 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_WITH_FMT`, **zlib** для `LOGIT_WITH_GZIP`, **zstd** для +`LOGIT_WITH_ZSTD`, **kurlyk** для `LOGIT_WITH_OTLP` и +**mdbx-containers** для `LOGIT_WITH_MDBX`. Их можно установить как пакеты, +подключить из собственной структуры зависимостей или включить +`LOGIT_USE_SUBMODULES=ON` для development-сборки. Для install/export нужны +внешние установленные/imported targets. + +### CMake subdirectory или vendored checkout + +```cmake +add_subdirectory(external/log-it-cpp) +target_link_libraries(my_app PRIVATE log-it-cpp::log-it-cpp) +``` + +### Установленный CMake-пакет + +```cmake +find_package(log-it-cpp CONFIG REQUIRED) +target_link_libraries(my_app PRIVATE log-it-cpp::log-it-cpp) +``` + +Сборка и установка: + +```bash +cmake -S . -B build -DLOGIT_CPP_BUILD_TESTS=OFF +cmake --build build +cmake --install build --prefix ./install +``` + +Для consumer-проекта добавьте `-DCMAKE_PREFIX_PATH=/path/to/install`. +`LOGIT_WITH_PROMETHEUS_SERVER=ON` и bundled optional dependencies пока +поддерживаются только в source/build-tree; install намеренно завершается +ошибкой вместо создания неработающего package. 4. (Необязательно) Включите макросы fmt: @@ -800,6 +896,11 @@ LogIt++ включает библиотеку *fmt* для форматиров - `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_CONTEXT` (по умолчанию: OFF) — включить MDC/NDC и context-токены форматтера. +- `LOGIT_WITH_OTLP` (по умолчанию: OFF, C++17) — OTLP/HTTP через kurlyk; не поддерживается в Emscripten. +- `LOGIT_WITH_PROMETHEUS` (по умолчанию: OFF) — Prometheus text payload; не поддерживается в Emscripten. +- `LOGIT_WITH_PROMETHEUS_SERVER` (по умолчанию: OFF, C++17) — встроенный Prometheus HTTP server; не поддерживается в Emscripten, install сейчас запрещён. +- `LOGIT_WITH_MDBX` (по умолчанию: OFF, C++17) — структурированное MDBX-хранилище через mdbx-containers; не поддерживается в Emscripten и MSVC. - `LOGIT_WITH_SYSLOG` (по умолчанию: ON на Unix-подобных системах) — сборка бэкенда syslog. - `LOGIT_WITH_WIN_EVENT_LOG` (по умолчанию: ON в Windows) — сборка бэкенда Windows Event Log. - `LOGIT_FORCE_ASYNC_OFF` (по умолчанию: OFF) — принудительно отключить асинхронное выполнение даже в многопоточных сборках. @@ -846,6 +947,20 @@ LogIt++ включает библиотеку *fmt* для форматиров - Адаптер LogIt кладёт номер слота в `LogRecord::line` (см. `bench/adapters/LogItAdapter.cpp`). Приёмник вызывает `LatencyRecorder::complete_slot()`, когда видит неотрицательный номер строки; никаких дополнительных полей в записи не требуется. +## Матрица бэкендов + +| Бэкенд | Включение | Standard | Зависимость | Ограничения | +|---|---|---:|---|---| +| Console, file, unique file, memory, crash | встроены | C++11 | TimeShield | Native и документированные Emscripten stubs | +| Syslog | `LOGIT_WITH_SYSLOG=ON` | C++11 | POSIX syslog | Unix-подобные системы | +| Windows Event Log | `LOGIT_WITH_WIN_EVENT_LOG=ON` | C++11 | Windows SDK | Только Windows | +| `WindowsDebugLogger` | встроен | C++11 | Windows API | `OutputDebugStringW` в Windows; в остальных системах fallback в stderr | +| OTLP/HTTP | `LOGIT_WITH_OTLP=ON` | C++17 | kurlyk | Не Emscripten; для install нужен внешний kurlyk | +| OTLP payload callback | `LOGIT_WITH_OTLP=ON` | C++17 | Для callback не нужен; общая OTLP-функция | JSON-сериализация и callback вызывающей стороны | +| Prometheus payload | `LOGIT_WITH_PROMETHEUS=ON` | C++11 | нет | Не Emscripten | +| Prometheus HTTP server | `LOGIT_WITH_PROMETHEUS_SERVER=ON` | C++17 | Simple-Web-Server/Asio | Только build-tree; install запрещён | +| MDBX | `LOGIT_WITH_MDBX=ON` | C++17 | mdbx-containers | Не Emscripten и не MSVC | + ## Системные бэкенды LogIt++ может отправлять сообщения в системные журналы. @@ -880,7 +995,7 @@ LOGIT_ERROR("Что-то пошло не так"); ## Документация -Подробную документацию для LogIt++, включая описание API и примеры использования, можно найти [здесь](https://newyaroslav.github.io/log-it-cpp/). +Подробную документацию для LogIt++, включая описание API и примеры использования, можно найти [здесь](https://liminode.github.io/log-it-cpp/). --- diff --git a/README.md b/README.md index 031779b..99bdb82 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,23 @@ ## Overview -**LogIt++** is a macro-first C++ logging library that supports C++11 and newer toolchains. It pairs lightweight instrumentation macros with configurable backends (console, rotating files, syslog, Windows Event Log, or custom sinks) and routes messages through an asynchronous queue so applications remain responsive while recording detailed diagnostics. The library combines the convenience of macro-driven logging similar to **IceCream-Cpp** with the configurability of engines such as **spdlog**. +**LogIt++** is a macro-first C++ logging library. The core and most built-in +backends support C++11; OTLP, Prometheus HTTP server, and MDBX integrations +require C++17. It pairs lightweight instrumentation macros with configurable +backends (console, rotating files, memory, system/crash loggers, OTLP, +Prometheus, MDBX, or custom sinks). Most general-purpose native backends are +asynchronous by default, while specialized backends may be synchronous or own +their own worker and queue. Key characteristics: - **Macro-oriented API.** Consistent macro families (`LOGIT_`, `LOGIT_PRINTF_`, `LOGIT_STREAM_`, etc.) cover immediate messages, printf-style formatting, streaming, throttling, and tagging. Defining `LOGIT_SHORT_NAME` when including `` enables compact aliases like `LOG_I`, `LOG_WPF`, and `LOG_S_INFO`. -- **Flexible formatting and routing.** Customize output patterns, mix console/file/system backends, or supply custom logger implementations. -- **Async by default.** Each backend is served by the task executor with configurable queue sizes and overflow policies, plus helpers such as `LOGIT_WARN_ONCE` or `LOGIT_ERROR_THROTTLE` to keep repeated messages under control. +- **Flexible formatting and routing.** Customize output patterns, mix console, + file, system, telemetry, and storage backends, or supply custom logger + implementations. +- **Configurable delivery.** General-purpose native backends use asynchronous + queues by default; queue limits, overflow policies, dedicated executors, + and synchronous modes are configurable per backend where supported. Normal usage goes through the public `LOGIT_*` / `LOG_*` macro families. Pick the family that matches your logging style: plain, `printf`, stream, @@ -55,6 +65,13 @@ Recent focused examples include: - `examples/example_logit_prometheus_server.cpp` - embedded `/metrics` endpoint with built-in and application metrics. - `examples/example_logit_mdc_ndc.cpp` - mapped and nested diagnostic context across scopes and threads. +Detailed backend and executor guides: + +- [`docs/OtlpHttpLogger.md`](docs/OtlpHttpLogger.md) — OTLP/HTTP and callback exporters, structured attributes, retries, splitting, and compression. +- [`docs/PrometheusLogger.md`](docs/PrometheusLogger.md) — payload/server backends, registry metrics, scrape configuration, and limitations. +- [`docs/TaskExecutor.md`](docs/TaskExecutor.md) — queue variants, overflow policies, hot resize, and lifecycle guarantees. +- [`docs/backpressure.md`](docs/backpressure.md) — application-facing queue tuning and drop counters. + ## Macro Examples ### Long-form macros @@ -418,6 +435,34 @@ LOGIT_SECTION("Proxy"); LOGIT_RAW("Proxy enabled: False"); ``` +- **Diagnostic Context (MDC/NDC)**: + +Enable `LOGIT_WITH_CONTEXT` to attach mapped and nested diagnostic context to +records and format it with `%K`, `%K{key}`, and `%J`. + +```cpp +LOGIT_MDC_PUT("request_id", "req-42"); +LOGIT_NDC_GUARD("checkout"); +LOGIT_INFO("processing order"); +``` + +- **Structured and telemetry backends**: + +Optional `LOGIT_WITH_MDBX` persists structured records and payloads through +`mdbx-containers`. `LOGIT_WITH_OTLP` enables OTLP exporters: `OtlpHttpLogger` +sends HTTP requests through kurlyk, while `OtlpPayloadLogger` delivers serialized +payloads through a callback. `LOGIT_WITH_PROMETHEUS` / +`LOGIT_WITH_PROMETHEUS_SERVER` provide callback and embedded `/metrics` +backends. See the dedicated guides above for setup and platform/package +limitations. + +- **Console stream routing and cleanup**: + +`ConsoleLogger::Config::routes` can route level ranges to `std::cout`, +`std::cerr`, or a caller-owned stream. `LOGIT_CLEAR_LOGGER` and +`LOGIT_CLEAR_ALL_LOGGERS` clear supported in-memory or persisted records and +return a `LogClearResult` describing the outcome. + - **Rotating File Logs**: Automatic file rotation based on size with optional asynchronous compression using gzip or zstd. @@ -439,7 +484,10 @@ Use the host OS logging facility. `SyslogLogger` works with POSIX `syslog`, whil - **Asynchronous Logging**: -Improve application performance with asynchronous logging. All loggers handle messages in a separate thread by default. +Most general-purpose native backends are asynchronous by default. Crash and +payload callback backends are synchronous, OTLP maintains its own exporter +queue, dedicated executors create one worker per selected backend, and +Emscripten without pthreads drains cooperatively without OS worker threads. - **Stream-Based Logging**: @@ -451,25 +499,9 @@ LOGIT_STREAM_INFO() << "Stream-based info logging with short macro. Integer valu - **Extensibility**: -Create custom loggers and formatters to meet your specific requirements. - -``` -class CustomLogger : public logit::ILogger { -public: - CustomLogger() = default; - - /// brief Logs a message by formatting the log record and message. - /// \param record The log record containing event details. - /// \param message The formatted log message to log. - void log(const logit::LogRecord& record, const std::string& message) override { - // Implementation for sending logs... - } - - ~CustomLogger() override = default; -}; - -LOGIT_ADD_LOGGER(CustomLogger, (), logit::SimpleLogFormatter, ("%v")); -``` +Create custom loggers and formatters to meet your specific requirements. See +[Custom Logger Backend and Formatter](#custom-logger-backend-and-formatter) +below for a complete implementation that matches the current interfaces. --- @@ -531,10 +563,12 @@ For more usage examples, please refer to the `examples` folder in the repository ### Compile-Time Log Level -You can exclude lower-severity logs from the binary by specifying the maximum level to compile. Define the `LOGIT_COMPILED_LEVEL` macro during compilation: +You can exclude lower-severity logs from the binary by specifying the minimum +severity compiled into the program. Define the `LOGIT_COMPILED_LEVEL` macro +during compilation: ```bash -g++ -DLOGIT_COMPILED_LEVEL=logit::LogLevel::LOG_LVL_WARN ... +g++ -DLOGIT_COMPILED_LEVEL=LOGIT_LEVEL_WARN ... ``` With the example above, `TRACE`, `DEBUG`, and `INFO` macros are turned into no-ops at compile time. @@ -830,10 +864,29 @@ public: void wait() override {} + std::string get_string_param(const logit::LoggerParam& param) const override { + (void)param; + return std::string(); + } + + int64_t get_int_param(const logit::LoggerParam& param) const override { + (void)param; + return 0; + } + + double get_float_param(const logit::LoggerParam& param) const override { + (void)param; + return 0.0; + } + + void set_log_level(logit::LogLevel level) override { m_log_level = level; } + logit::LogLevel get_log_level() const override { return m_log_level; } + private: std::string m_file_name; std::ofstream m_log_file; std::mutex m_mutex; + logit::LogLevel m_log_level = logit::LogLevel::LOG_LVL_TRACE; }; ``` @@ -845,6 +898,10 @@ private: class JsonLogFormatter : public logit::ILogFormatter { public: + void set_timestamp_offset(int64_t offset_ms) override { + (void)offset_ms; + } + std::string format(const logit::LogRecord& record) const override { Json::Value log_entry; log_entry["level"] = static_cast(record.log_level); @@ -885,6 +942,7 @@ public: | `LOGIT_SCOPE_(phase)` / `LOGIT_SCOPE__T(threshold_ms, phase)` | RAII scope-duration logging, optionally only when a threshold is exceeded. | | `LOGIT_SCOPE_PRINTF_(...)` / `LOGIT_SCOPE_PRINTF__T(...)` | Scope timers with `printf`-style formatting. | | `LOGIT_SCOPE_FMT_(...)` / `LOGIT_SCOPE_FMT__T(...)` | Scope timers with `fmt`-style formatting. | +| `LOGIT_CLEAR_LOGGER(index)` / `LOGIT_CLEAR_ALL_LOGGERS()` | Clear supported logger-owned records and return a `LogClearResult`; `_EX` variants accept `LogClearOptions`. | | `LOGIT_PERROR_(msg)`, `LOGIT_WINERR_(msg)`, `LOGIT_SYSERR_(msg)` | Append decoded platform error information to a message. | | `LOGIT_ADD_LOGGER(...)` and `LOGIT_ADD_*` backend macros | Register console, memory, file, unique-file, crash, syslog, event-log, or custom backends. | | `LOGIT_GET_*`, `LOGIT_SET_*`, `LOGIT_IS_*`, `LOGIT_WAIT()`, `LOGIT_SHUTDOWN()` | Query and manage logger/task-executor state. | @@ -951,7 +1009,9 @@ the rows above document the canonical public families. ## Installation -LogIt++ is a header-only library. To integrate it into your project, follow these steps: +LogIt++ itself is header-only. When consumed through the CMake target, enabled +optional features may add transitive compile and link dependencies. Choose one +of the following integration paths. 1. Clone the repository with its submodules: @@ -968,7 +1028,40 @@ git clone --recurse-submodules https://github.com/LimiNode/log-it-cpp.git CMake builds first look for the required **TimeShield** package and then fall back to this repository's bundled `external/time-shield-cpp` submodule when it is present. If you vendor dependencies inside your own project, use your own install or vendor paths; the directory does not need to be named `external`. -Optional dependencies are needed only for the features you enable: **fmt** for `LOGIT_WITH_FMT`, **zlib** for `LOGIT_WITH_GZIP`, and **zstd** for `LOGIT_WITH_ZSTD`. Install them as packages, provide them from your own dependency layout, or set `LOGIT_USE_SUBMODULES=ON` to let CMake use the bundled copies from this repository. +Optional dependencies are needed only for the features you enable: **fmt** for +`LOGIT_WITH_FMT`, **zlib** for `LOGIT_WITH_GZIP`, **zstd** for +`LOGIT_WITH_ZSTD`, **kurlyk** for `LOGIT_WITH_OTLP`, and +**mdbx-containers** for `LOGIT_WITH_MDBX`. Install them as packages, provide +them from your own dependency layout, or set `LOGIT_USE_SUBMODULES=ON` to let +CMake use bundled copies for development. Installed package exports require +optional dependencies to be provided as installed/imported targets. + +### CMake subdirectory or vendored checkout + +```cmake +add_subdirectory(external/log-it-cpp) +target_link_libraries(my_app PRIVATE log-it-cpp::log-it-cpp) +``` + +### Installed CMake package + +```cmake +find_package(log-it-cpp CONFIG REQUIRED) +target_link_libraries(my_app PRIVATE log-it-cpp::log-it-cpp) +``` + +Build and install the package with: + +```bash +cmake -S . -B build -DLOGIT_CPP_BUILD_TESTS=OFF +cmake --build build +cmake --install build --prefix ./install +``` + +Pass `-DCMAKE_PREFIX_PATH=/path/to/install` when configuring the consumer. +`LOGIT_WITH_PROMETHEUS_SERVER=ON` and bundled optional dependency targets are +currently supported for source/build-tree development only; the install step +rejects them rather than exporting a broken package. 4. (Optional) Enable fmt-style macros: @@ -984,6 +1077,10 @@ The following toggles cover all build-time features: - `LOGIT_WITH_GZIP` / `LOGIT_WITH_ZSTD` (defaults: OFF) — enable gzip or zstd support for rotated files. - `LOGIT_WITH_FMT` (default: OFF) — include the `{}`-style formatting macros. - `LOGIT_WITH_CONTEXT` (default: OFF) — enable MDC/NDC helpers and `%K`, `%K{key}`, `%J` formatter tokens. +- `LOGIT_WITH_OTLP` (default: OFF, C++17) — enable OTLP/HTTP export through kurlyk; not supported on Emscripten. +- `LOGIT_WITH_PROMETHEUS` (default: OFF) — enable Prometheus text payload support; not supported on Emscripten. +- `LOGIT_WITH_PROMETHEUS_SERVER` (default: OFF, C++17) — enable the embedded Prometheus HTTP server; not supported on Emscripten and currently rejected by `cmake --install`. +- `LOGIT_WITH_MDBX` (default: OFF, C++17) — enable structured MDBX storage through mdbx-containers; not supported on Emscripten or MSVC. - `LOGIT_USE_SUBMODULES` (default: OFF) allows bundled optional dependency fallbacks such as fmt, zlib, and zstd when system packages are missing. - `LOGIT_WITH_SYSLOG` (default: ON on Unix-like targets) — build the syslog backend. - `LOGIT_WITH_WIN_EVENT_LOG` (default: ON on Windows) — build the Windows Event Log backend. @@ -992,6 +1089,20 @@ The following toggles cover all build-time features: - `LOGIT_ENABLE_DROP_OLDEST_SLOWPATH` (default: ON) — compile the slow-path used by `DropOldest` when the ring is full. - `LOGIT_EMSCRIPTEN` (default: ON under Emscripten toolchains) — adjust the build for single-threaded WebAssembly environments. +## Backend matrix + +| Backend | Enablement | Standard | Extra dependency | Platform/package notes | +|---|---|---:|---|---| +| Console, file, unique file, memory, crash | built in | C++11 | TimeShield | Native and Emscripten stubs where documented | +| Syslog | `LOGIT_WITH_SYSLOG=ON` | C++11 | POSIX syslog | Unix-like platforms | +| Windows Event Log | `LOGIT_WITH_WIN_EVENT_LOG=ON` | C++11 | Windows SDK | Windows only | +| `WindowsDebugLogger` | built in | C++11 | Windows API | Windows `OutputDebugStringW`; stderr fallback elsewhere | +| OTLP/HTTP | `LOGIT_WITH_OTLP=ON` | C++17 | kurlyk | Not supported on Emscripten; installed exports need external kurlyk | +| OTLP payload callback | `LOGIT_WITH_OTLP=ON` | C++17 | None for callback; shared OTLP feature | Serializes JSON and invokes the caller callback | +| Prometheus payload | `LOGIT_WITH_PROMETHEUS=ON` | C++11 | None | Not supported on Emscripten | +| Prometheus HTTP server | `LOGIT_WITH_PROMETHEUS_SERVER=ON` | C++17 | Simple-Web-Server/Asio | Build-tree only; install currently rejected | +| MDBX structured storage | `LOGIT_WITH_MDBX=ON` | C++17 | mdbx-containers | Not supported on Emscripten or MSVC | + ## System Backends LogIt++ can forward messages to system logging facilities. @@ -1088,7 +1199,7 @@ end-to-end timing across producers/consumers. ## Documentation -Detailed documentation for LogIt++, including API reference and usage examples, can be found [here](https://newyaroslav.github.io/log-it-cpp/). +Detailed documentation for LogIt++, including API reference and usage examples, can be found [here](https://liminode.github.io/log-it-cpp/). --- diff --git a/docs/OtlpHttpLogger.md b/docs/OtlpHttpLogger.md index 4b6b18d..36ea03b 100644 --- a/docs/OtlpHttpLogger.md +++ b/docs/OtlpHttpLogger.md @@ -1,3 +1,5 @@ +\page otlp_http_logger OTLP/HTTP logger + # OTLP/HTTP logger `OtlpHttpLogger` is an optional LogIt++ backend that exports log records to an OpenTelemetry-compatible OTLP/HTTP endpoint. @@ -51,6 +53,39 @@ int main() { } ``` +## `OtlpPayloadLogger` callback exporter + +`OtlpPayloadLogger` shares the OTLP JSON serializer and structured-attribute +configuration with `OtlpHttpLogger`, but does not create an HTTP client. It +passes each serialized payload chunk to `Config::on_payload`, so applications +can use their own HTTP transport, message broker, or collector adapter. + +```cpp +#include + +logit::OtlpPayloadLogger::Config config; +config.format.service_name = "trade-bot"; +config.max_batch_size = 128; +config.max_payload_bytes = 512 * 1024; +config.on_payload = [](std::string payload) { + // Forward the payload through the application's transport. +}; + +LOGIT_ADD_LOGGER( + logit::OtlpPayloadLogger, + (config), + logit::SimpleLogFormatter, + ("%v") +); +``` + +The callback exporter is asynchronous by default and owns a bounded queue. +Set `async = false` for synchronous callback delivery, or set +`drop_on_overflow = false` to apply producer-side backpressure. Large batches +are split at `max_payload_bytes`; each chunk is delivered independently. The +logger does not retry callback failures, but counts them in +`LoggerParam::FailedExportCount`. + ## Export model The backend sends OTLP/HTTP JSON requests: diff --git a/docs/PrometheusLogger.md b/docs/PrometheusLogger.md index 77c2146..47cf5ec 100644 --- a/docs/PrometheusLogger.md +++ b/docs/PrometheusLogger.md @@ -1,3 +1,5 @@ +\page prometheus_logger Prometheus Logger + # Prometheus Logger ## Overview @@ -6,8 +8,8 @@ LogIt++ provides two Prometheus backends for exposing internal log metrics in th [Prometheus text exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/): - **PrometheusPayloadLogger** -- callback-based; delivers the serialized payload to a - user-provided function. Useful when you have your own HTTP server or want to push to - a Prometheus Pushgateway. + user-provided function. It can be integrated with your own HTTP server, + Pushgateway client, or another existing transport. - **PrometheusHttpServerLogger** -- embedded HTTP server; serves `/metrics` on a configurable port using Simple-Web-Server. Ideal for simple services without a @@ -45,6 +47,11 @@ option(LOGIT_WITH_PROMETHEUS_SERVER "Enable Prometheus HTTP server backend" OFF) `LOGIT_WITH_PROMETHEUS_SERVER` implies `LOGIT_WITH_PROMETHEUS` and requires C++17 (Simple-Web-Server dependency). +`LOGIT_WITH_PROMETHEUS_SERVER=ON` currently supports source-tree and build-tree +consumption only. The install/export step intentionally rejects this +configuration because the Simple-Web-Server and Asio include trees are not +exported package dependencies yet. + ## Usage: PrometheusPayloadLogger For a runnable callback example with custom application metrics, see diff --git a/docs/TaskExecutor.md b/docs/TaskExecutor.md index f9dd95c..be1dacd 100644 --- a/docs/TaskExecutor.md +++ b/docs/TaskExecutor.md @@ -1,10 +1,14 @@ +\page task_executor TaskExecutor Implementation Notes + # TaskExecutor Implementation Notes -The asynchronous task executor powers every non-blocking logger. It accepts -work from multiple producer threads and drains it on a dedicated worker. This -document describes how the executor behaves across build configurations, -provides guidance on tuning the backpressure policies, and explains the -lifetime guarantees that logger integrations rely on. +The global `TaskExecutor` powers backends that use LogIt++'s shared +asynchronous executor. Some backends are synchronous, dedicated-executor +configurations use per-backend `SingleThreadExecutor` instances, and OTLP +backends maintain their own exporter queues. This document describes how the +shared executor behaves across build configurations, provides guidance on +tuning backpressure policies, and explains the lifetime guarantees that logger +integrations rely on. ## 1. Implementation variants diff --git a/docs/backpressure.md b/docs/backpressure.md index d6072dd..6375fde 100644 --- a/docs/backpressure.md +++ b/docs/backpressure.md @@ -1,7 +1,11 @@ +\page backpressure Queue Back-Pressure Controls + # Queue Back-Pressure Controls -The asynchronous task executor backs every logger and can be tuned to handle -high-load bursts. Use the following helpers from +The global asynchronous task executor backs loggers that use the default +executor and can be tuned to handle high-load bursts. Some backends are +synchronous, while configured dedicated executors and OTLP maintain their own +queues. Use the following helpers from `` when preparing stress tests or long-running services: @@ -22,7 +26,8 @@ publishing policy decides to discard work. Combining the counter with `TaskExecutor::wait()` makes it easy to assert the expected throughput for each policy without inspecting private state. -The queue limits apply globally to every logger instance. After finishing a -burst test remember to restore the capacity or shut down the logging subsystem -with `LOGIT_WAIT()` and `LOGIT_SHUTDOWN()` to avoid interfering with other -scenarios. +These global controls affect the shared `TaskExecutor` only. A backend with +`Config::use_dedicated_executor=true` has its own capacity and policy, and +OTLP uses its exporter queue settings. After finishing a burst test remember +to restore the shared capacity or shut down the logging subsystem with +`LOGIT_WAIT()` and `LOGIT_SHUTDOWN()` to avoid interfering with other scenarios. diff --git a/docs/groups.dox b/docs/groups.dox index 14d4c72..e77a929 100644 --- a/docs/groups.dox +++ b/docs/groups.dox @@ -87,7 +87,15 @@ log messages are processed and stored: - \b ConsoleLogger: Outputs logs to the console with optional color coding. - \b FileLogger: Logs messages to files with date-based rotation and old file deletion. - \b UniqueFileLogger: Writes each log message to a unique file with automatic cleanup. +- \b MemoryLogger: Keeps a bounded in-memory snapshot buffer and supports subscriptions. +- \b MdbxLogger: Persists structured records and payloads in MDBX when enabled. +- \b OtlpHttpLogger: Exports records to an OTLP/HTTP endpoint when enabled. +- \b OtlpPayloadLogger: Exposes OTLP payloads through a callback when enabled. +- \b PrometheusPayloadLogger: Emits Prometheus text payloads through a callback when enabled. +- \b PrometheusHttpServerLogger: Serves Prometheus metrics over HTTP when enabled. +- \b CrashLogger: Provides platform-specific crash-time logging backends. +- \b WindowsDebugLogger: Writes to the Windows debugger output or stderr fallback. - \b SyslogLogger: Sends log messages to the POSIX syslog service. - \b EventLogLogger: Writes logs to the Windows Event Log. - \b SystemLogger: Alias that maps to the platform's system logger. -*/ \ No newline at end of file +*/ diff --git a/docs/mainpage.dox b/docs/mainpage.dox index 64773f4..cbfeff2 100644 --- a/docs/mainpage.dox +++ b/docs/mainpage.dox @@ -5,16 +5,30 @@ Version: VERSION_PLACEHOLDER \section overview_sec Overview -`LogIt++` is a macro-first C++ logging library that supports C++11 and newer toolchains. It pairs lightweight instrumentation macros with configurable backends (console, rotating files, syslog, Windows Event Log, or custom sinks) and routes messages through an asynchronous queue so applications remain responsive while recording detailed diagnostics. The library combines the convenience of macro-driven logging similar to **IceCream-Cpp** with the configurability of engines such as **spdlog**. +`LogIt++` is a macro-first C++ logging library. The core and most built-in +backends support C++11; OTLP, Prometheus HTTP server, and MDBX integrations +require C++17. It pairs lightweight instrumentation macros with configurable +backends (console, rotating files, memory, system/crash loggers, OTLP, +Prometheus, MDBX, or custom sinks). Most general-purpose native backends are +asynchronous by default, while specialized backends may be synchronous or own +their own worker and queue. Key characteristics: - **Macro-oriented API.** Consistent macro families (`LOGIT_`, `LOGIT_PRINTF_`, `LOGIT_STREAM_`, etc.) cover immediate messages, `printf`-style formatting, streaming, throttling, and tagging. Defining `LOGIT_SHORT_NAME` when including `` enables compact aliases like `LOG_I`, `LOG_WPF`, and `LOG_S_INFO`. -- **Flexible formatting and routing.** Customize output patterns, mix console/file/system backends, or supply custom logger implementations. -- **Async by default.** Each backend is served by the task executor with configurable queue sizes and overflow policies, plus helpers such as `LOGIT_WARN_ONCE` or `LOGIT_ERROR_THROTTLE` to keep repeated messages under control. +- **Flexible formatting and routing.** Customize output patterns, mix console, + file, system, telemetry, and storage backends, or supply custom logger + implementations. +- **Configurable delivery.** General-purpose native backends use asynchronous + queues by default; queue limits, overflow policies, dedicated executors, + and synchronous modes are configurable per backend where supported. See the macro examples below or browse the `examples/` directory for focused demonstrations, including queue tuning and crash handling. `examples/example_logit_memory_logger.cpp` and `examples/example_logit_mdbx_logger.cpp` show the shared read/callback API for in-memory and MDBX-backed stored logs. +The `logit::detail` namespace is implementation-only and carries no source or +API compatibility guarantee. It is shown in the reference for maintainers; +application code should use the public umbrella headers and macros. + \section macro_first_usage_sec Macro-first usage For normal application logging, prefer the public macros from `` or @@ -87,6 +101,34 @@ void short_names_demo() { For a standalone program that brings everything together and intentionally aborts after logging a fatal message, check `examples/example_logit_minimal_crash.cpp`. +\subsection macro_examples_context Diagnostic context (MDC/NDC) + +Enable `LOGIT_WITH_CONTEXT` to keep mapped key/value context and a nested +diagnostic context stack in thread-local storage. Use `%K`, `%K{key}`, and `%J` +pattern tokens to render the values. + +\code{.cpp} +#include + +void handle_request() { + LOGIT_MDC_PUT("request_id", "req-42"); + { + LOGIT_NDC_GUARD("checkout"); + LOGIT_INFO("processing order"); + } + LOGIT_MDC_CLEAR(); +} +\endcode + +\subsection macro_examples_optional Optional backends + +The `MemoryLogger`, `MdbxLogger`, OTLP, and Prometheus backends are demonstrated +in the corresponding files under `examples/`. OTLP requires +`LOGIT_WITH_OTLP=ON`, Prometheus payload/server requires +`LOGIT_WITH_PROMETHEUS=ON` or `LOGIT_WITH_PROMETHEUS_SERVER=ON`, and MDBX +requires `LOGIT_WITH_MDBX=ON`. See `\ref otlp_http_logger` and +`\ref prometheus_logger` for backend-specific configuration and limitations. + \subsection macro_examples_targeted Targeted, conditional, and scope helpers Beyond the base logging calls, LogIt++ exposes canonical helper families for @@ -265,7 +307,11 @@ LOGIT_ADD_EVENT_LOG_DEFAULT(); \subsection async_logging Asynchronous Logging -Improve application performance with asynchronous logging. All loggers handle messages in a separate thread by default. +Most general-purpose native backends handle messages asynchronously by +default. Crash and payload callback backends are synchronous, OTLP maintains +its own exporter queue, and dedicated executors create one worker per selected +backend. Emscripten builds without pthreads drain cooperatively without OS +worker threads. \subsection buffer_modes Queue Buffer Modes @@ -313,10 +359,12 @@ LOGIT_STREAM_INFO() << "Stream-based info logging with short macro. Integer valu \subsection compile_level Compile-Time Log Level -Exclude lower-severity logs from the final binary by defining the maximum level to compile. Set the `LOGIT_COMPILED_LEVEL` macro during compilation: +Exclude lower-severity logs from the final binary by defining the minimum +severity compiled into the program. Set the `LOGIT_COMPILED_LEVEL` macro during +compilation: \code{.bash} -g++ -DLOGIT_COMPILED_LEVEL=logit::LogLevel::LOG_LVL_WARN ... +g++ -DLOGIT_COMPILED_LEVEL=LOGIT_LEVEL_WARN ... \endcode With this configuration, `TRACE`, `DEBUG`, and `INFO` macros are disabled at compile time. @@ -328,24 +376,8 @@ cannot re-enable macros removed earlier by `LOGIT_COMPILED_LEVEL`. \subsection extensibility Extensibility Create custom loggers and formatters to meet your specific requirements. - -\code{.cpp} -class CustomLogger : public logit::ILogger { -public: - CustomLogger() = default; - - /// \brief Logs a message by formatting the log record and message. - /// \param record The log record containing event details. - /// \param message The formatted log message to log. - void log(const logit::LogRecord& record, const std::string& message) override { - // Implementation for sending logs... - } - - ~CustomLogger() override = default; -}; - -LOGIT_ADD_LOGGER(CustomLogger, (), logit::SimpleLogFormatter, ("%v")); -\endcode +See the complete implementation in `\ref custom_backend_sec` below; it matches +the current `ILogger` and `ILogFormatter` interfaces. \section usage_sec Usage @@ -743,7 +775,7 @@ Controls how frequently blocking producers poll for capacity while `LOGIT_QUEUE_BLOCK` is active. \code{.cpp} -#define LOGIT_TASK_EXECUTOR_BLOCK_WAIT_USEC 500 +#define LOGIT_TASK_EXECUTOR_BLOCK_WAIT_USEC 200 \endcode - **LOGIT_TASK_EXECUTOR_DRAIN_BUDGET**: @@ -752,7 +784,7 @@ Defines how many queued tasks a worker drains per iteration in ring-buffer builds before yielding. \code{.cpp} -#define LOGIT_TASK_EXECUTOR_DRAIN_BUDGET 4096 +#define LOGIT_TASK_EXECUTOR_DRAIN_BUDGET 2048 \endcode - **LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY**: @@ -761,7 +793,7 @@ Defines the default MPSC ring capacity used when unlimited queue mode still needs a backing ring size. \code{.cpp} -#define LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY 2048 +#define LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY 1024 \endcode - **LOGIT_SHORT_NAME**: @@ -784,7 +816,10 @@ other infrastructure code that intentionally works below the macro layer. \subsection custom_logger Custom Logger Example -To create a custom logger backend, you need to implement the `ILogger` interface, which requires defining the `log()` and `wait()` methods. Here's an example of a simple custom logger that logs messages to a text file: +To create a custom logger backend, implement all pure virtual methods of the +`ILogger` interface (`log()`, parameter accessors, log-level accessors, and +`wait()`). The complete example below logs messages to a text file and matches +the current interface: \code{.cpp} #include @@ -817,10 +852,34 @@ public: // No async processing, so no need to implement } + std::string get_string_param(const logit::LoggerParam& param) const override { + (void)param; + return std::string(); + } + + int64_t get_int_param(const logit::LoggerParam& param) const override { + (void)param; + return 0; + } + + double get_float_param(const logit::LoggerParam& param) const override { + (void)param; + return 0.0; + } + + void set_log_level(logit::LogLevel level) override { + m_log_level = level; + } + + logit::LogLevel get_log_level() const override { + return m_log_level; + } + private: std::string m_file_name; ///< The name of the log file std::ofstream m_log_file; ///< The file stream for logging std::mutex m_mutex; ///< Mutex to ensure thread-safe logging + logit::LogLevel m_log_level = logit::LogLevel::LOG_LVL_TRACE; }; \endcode @@ -834,8 +893,8 @@ LOGIT_ADD_LOGGER( // or, if you intentionally need the lower-level equivalent... logit::Logger::get_instance().add_logger( - std::make_unique("logfile.txt"), - std::make_unique()); + std::unique_ptr(new FileLogger("logfile.txt")), + std::unique_ptr(new logit::SimpleLogFormatter())); \endcode Prefer `LOGIT_ADD_LOGGER(...)` when it fits your use case; calling @@ -852,6 +911,10 @@ In addition to creating custom loggers, you can also create custom formatters by class JsonLogFormatter : public logit::ILogFormatter { public: + void set_timestamp_offset(int64_t offset_ms) override { + (void)offset_ms; + } + // Formats the log record into a JSON string std::string format(const logit::LogRecord& record) const override { Json::Value log_entry; @@ -873,13 +936,13 @@ This `JsonLogFormatter` formats log messages as JSON objects. You can combine it \code{.cpp} LOGIT_ADD_LOGGER( FileLogger, ("logfile.json"), - logit::JsonLogFormatter, ()); + JsonLogFormatter, ()); // or, if you intentionally need the lower-level equivalent... logit::Logger::get_instance().add_logger( - std::make_unique("logfile.json"), - std::make_unique()); + std::unique_ptr(new FileLogger("logfile.json")), + std::unique_ptr(new JsonLogFormatter())); \endcode \subsection summary_custom_backend Summary @@ -894,11 +957,16 @@ Here's a quick summary: \section install_sec Installation -LogIt++ is a header-only library, which means it can be easily included in your project without the need for compilation or linking. Below are the steps to integrate it into your project. +LogIt++ itself is header-only. When consumed through the CMake target, +enabled optional features may add transitive compile and link dependencies. +Below are the steps to integrate it into your project. \subsection step1 Step 1: Clone the Repository -First, clone the LogIt++ repository from GitHub along with its submodules. The required dependency is **time-shield-cpp** (for time utilities); optional features may also use **fmt**, **zlib**, or **zstd**. +First, clone the LogIt++ repository from GitHub along with its submodules. The +required dependency is **time-shield-cpp** (for time utilities); optional +features may also use **fmt**, **zlib**, **zstd**, **kurlyk**, or +**mdbx-containers**. To clone the repository with submodules, use the following command: @@ -927,7 +995,13 @@ This will give you access to the entire logging system. CMake builds first look for the required **TimeShield** package and then fall back to this repository's bundled `external/time-shield-cpp` submodule when it is present. If you vendor dependencies inside your own project, use your own install or vendor paths; the directory does not need to be named `external`. -Optional dependencies are needed only for the features you enable: **fmt** for `LOGIT_WITH_FMT`, **zlib** for `LOGIT_WITH_GZIP`, and **zstd** for `LOGIT_WITH_ZSTD`. Install them as packages, provide them from your own dependency layout, or set `LOGIT_USE_SUBMODULES=ON` to let CMake use the bundled copies from this repository. +Optional dependencies are needed only for the features you enable: **fmt** for +`LOGIT_WITH_FMT`, **zlib** for `LOGIT_WITH_GZIP`, **zstd** for +`LOGIT_WITH_ZSTD`, **kurlyk** for `LOGIT_WITH_OTLP`, and +**mdbx-containers** for `LOGIT_WITH_MDBX`. Install them as packages, provide +them from your own dependency layout, or set `LOGIT_USE_SUBMODULES=ON` to let +CMake use bundled copies for development. Installed package exports require +those dependencies to be provided as installed/imported targets. \subsection step4 Step 4: Using fmt (Optional) @@ -942,6 +1016,11 @@ All build-time toggles: - `LOGIT_BENCH_ENABLE` (default: OFF) — build the benchmarks; `LOGIT_BENCH_WITH_SPDLOG` (default: OFF) adds the spdlog comparison binaries. - `LOGIT_WITH_GZIP` / `LOGIT_WITH_ZSTD` (defaults: OFF) — enable gzip or zstd support for rotated files. - `LOGIT_WITH_FMT` (default: OFF) — include the `{}`-style formatting macros; `LOGIT_USE_SUBMODULES` (default: OFF) allows bundled optional dependency fallbacks such as fmt, zlib, and zstd when system packages are missing. +- `LOGIT_WITH_CONTEXT` (default: OFF) — enable MDC/NDC helpers and context formatter tokens. +- `LOGIT_WITH_OTLP` (default: OFF, C++17) — enable OTLP/HTTP export through kurlyk; not supported on Emscripten. +- `LOGIT_WITH_PROMETHEUS` (default: OFF) — enable Prometheus text payload support; not supported on Emscripten. +- `LOGIT_WITH_PROMETHEUS_SERVER` (default: OFF, C++17) — enable the embedded Prometheus HTTP server; not supported on Emscripten and currently rejected by `cmake --install`. +- `LOGIT_WITH_MDBX` (default: OFF, C++17) — enable structured MDBX storage through mdbx-containers; not supported on Emscripten or MSVC. - `LOGIT_WITH_SYSLOG` (default: ON on Unix-like targets) — build the syslog backend. - `LOGIT_WITH_WIN_EVENT_LOG` (default: ON on Windows) — build the Windows Event Log backend. - `LOGIT_FORCE_ASYNC_OFF` (default: OFF) — force synchronous logging even on multi-threaded builds. @@ -949,6 +1028,10 @@ All build-time toggles: - `LOGIT_ENABLE_DROP_OLDEST_SLOWPATH` (default: ON) — include the drop-oldest slow-path used when the ring is full. - `LOGIT_EMSCRIPTEN` (default: ON when building under Emscripten) — adjust the build for single-threaded WebAssembly targets. +Detailed guides for the optional backends and queue internals are available as +separate pages: `\ref otlp_http_logger`, `\ref prometheus_logger`, +`\ref task_executor`, and `\ref backpressure`. + \section bench_sec Benchmarks Run `./build/bench/logit_bench` to capture the full matrix (sync/async × null/file × producer counts × message sizes). Results are appended to `bench/results/latency.csv` with one row per library/combination. Adjust the workload with `LOGIT_BENCH_TOTAL` and `LOGIT_BENCH_WARMUP` if you need a lighter pass. @@ -987,7 +1070,18 @@ Async results include enqueue + worker wakeup/scheduling + sink time. File sinks \subsection step5 Step 5: Build and Run Your Project -After adding the necessary include paths, you can proceed to build and run your project. LogIt++ is designed to be easy to integrate and requires no linking since it's header-only. +After adding the necessary include paths, you can proceed to build and run your +project. For a vendored or installed CMake package, link the exported target: + +\code{.cmake} +find_package(log-it-cpp CONFIG REQUIRED) +target_link_libraries(my_app PRIVATE log-it-cpp::log-it-cpp) +\endcode + +The repository can be installed with `cmake --install`; use separately +installed dependency targets for optional features. `LOGIT_WITH_PROMETHEUS_SERVER` +and bundled optional dependencies are intentionally rejected by the install +step until their exported dependency targets are available. \section repo_sec Repository diff --git a/include/logit_cpp/logit/config.hpp b/include/logit_cpp/logit/config.hpp index 6fd1c10..ddc1759 100644 --- a/include/logit_cpp/logit/config.hpp +++ b/include/logit_cpp/logit/config.hpp @@ -5,7 +5,7 @@ /// \file config.hpp /// \brief Configuration macros for the LogIt logging system. -/// \ingroup ConfigMacros Configuration Macros +/// \ingroup ConfigMacros /// \{ #ifndef LOGIT_BASE_PATH diff --git a/include/logit_cpp/logit/detail/TaskExecutor.hpp b/include/logit_cpp/logit/detail/TaskExecutor.hpp index 516f9b7..428ecc5 100644 --- a/include/logit_cpp/logit/detail/TaskExecutor.hpp +++ b/include/logit_cpp/logit/detail/TaskExecutor.hpp @@ -39,7 +39,7 @@ namespace logit { namespace detail { /// \brief Simplified task executor for single-threaded Emscripten builds. /// \details The Emscripten variant keeps behaviour compatible with the /// browser event loop. See docs/TaskExecutor.md for the high level design. - /// \thread_safety Not thread-safe. + /// \note Not thread-safe in the single-threaded Emscripten build. class TaskExecutor { public: /// \brief Returns the singleton executor instance. @@ -166,7 +166,8 @@ namespace logit { namespace detail { /// \brief Thread-safe task executor backed by a dedicated worker thread. /// \details The full design, including backpressure semantics and hot /// resizing, is described in docs/TaskExecutor.md. - /// \thread_safety Thread-safe. + /// \note Thread-safe on native builds; Emscripten without pthreads is + /// single-threaded and uses cooperative draining. class TaskExecutor { public: /// \brief Returns the global executor instance. diff --git a/include/logit_cpp/logit/formatter/compiler/PatternCompiler.hpp b/include/logit_cpp/logit/formatter/compiler/PatternCompiler.hpp index caa3c04..706a27a 100644 --- a/include/logit_cpp/logit/formatter/compiler/PatternCompiler.hpp +++ b/include/logit_cpp/logit/formatter/compiler/PatternCompiler.hpp @@ -111,6 +111,7 @@ namespace logit { /// \param center Center alignment flag. /// \param trunc Truncation flag. /// \param strip_ansi If true, removes ANSI escape codes (e.g., colors). + /// \param context_key Optional MDC key for context formatting. explicit FormatInstruction( CompileContext context, FormatType type, diff --git a/include/logit_cpp/logit/loggers/CrashWindowsLogger.hpp b/include/logit_cpp/logit/loggers/CrashWindowsLogger.hpp index 2b896a4..9075a64 100644 --- a/include/logit_cpp/logit/loggers/CrashWindowsLogger.hpp +++ b/include/logit_cpp/logit/loggers/CrashWindowsLogger.hpp @@ -30,7 +30,7 @@ namespace logit { #if defined(_WIN32) /// \\class CrashWindowsLogger - /// \\ingroup LogBackends + /// \ingroup LogBackends /// \\brief Maintains an in-memory ring buffer of recent messages and dumps it on crashes. class CrashWindowsLogger : public ILogger { public: diff --git a/include/logit_cpp/logit/loggers/EventLogLogger.hpp b/include/logit_cpp/logit/loggers/EventLogLogger.hpp index 6682b41..5d0f649 100644 --- a/include/logit_cpp/logit/loggers/EventLogLogger.hpp +++ b/include/logit_cpp/logit/loggers/EventLogLogger.hpp @@ -24,7 +24,7 @@ namespace logit { /// \class EventLogLogger /// \brief Logger forwarding messages to Windows Event Log. - /// \thread_safety Thread-safe. + /// \note Thread-safe when enabled on Windows. class EventLogLogger : public ILogger { public: /// \brief Runtime configuration. diff --git a/include/logit_cpp/logit/loggers/ILogger.hpp b/include/logit_cpp/logit/loggers/ILogger.hpp index 8e69c84..8cc9b51 100644 --- a/include/logit_cpp/logit/loggers/ILogger.hpp +++ b/include/logit_cpp/logit/loggers/ILogger.hpp @@ -5,7 +5,7 @@ /// \file ILogger.hpp /// \brief Defines the interface for loggers used in the logging system. -/// \ingroup LogBackends Logging Backends +/// \ingroup LogBackends /// \{ #include diff --git a/include/logit_cpp/logit/loggers/SyslogLogger.hpp b/include/logit_cpp/logit/loggers/SyslogLogger.hpp index d2782dc..924cfa0 100644 --- a/include/logit_cpp/logit/loggers/SyslogLogger.hpp +++ b/include/logit_cpp/logit/loggers/SyslogLogger.hpp @@ -24,7 +24,7 @@ namespace logit { /// \class SyslogLogger /// \brief Logger forwarding messages to syslog. - /// \thread_safety Thread-safe. + /// \note Thread-safe when enabled on POSIX platforms. class SyslogLogger : public ILogger { public: /// \brief Runtime configuration. diff --git a/include/logit_cpp/logit/utils/argument_utils.hpp b/include/logit_cpp/logit/utils/argument_utils.hpp index 8e19bcb..ff48165 100644 --- a/include/logit_cpp/logit/utils/argument_utils.hpp +++ b/include/logit_cpp/logit/utils/argument_utils.hpp @@ -11,7 +11,6 @@ namespace logit { /// \brief Base case of recursion for argument conversion — when there are no more arguments. - /// \param name_iter Iterator for the argument name list. /// \return An empty vector, as there are no more arguments to process. inline std::vector args_to_array(std::vector::const_iterator /*name_iter*/) { return {};