![]() |
LogIt++
Header-only, macro-first C++ logging library
|
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:
LOGIT_<LEVEL>, LOGIT_PRINTF_<LEVEL>, LOGIT_STREAM_<LEVEL>, etc.) cover immediate messages, printf-style formatting, streaming, throttling, and tagging. Defining LOGIT_SHORT_NAME when including <logit.hpp> enables compact aliases like LOG_I, LOG_WPF, and LOG_S_INFO.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.
For normal application logging, include <logit.hpp> and use the public macros. Choose the matching family (LOGIT_<LEVEL>, LOGIT_PRINTF_<LEVEL>, LOGIT_STREAM_<LEVEL>, LOGIT_<LEVEL>_IF, LOGIT_<LEVEL>_TO, LOGIT_SCOPE_<LEVEL>, and related helpers) and pass the data directly to that macro.
Avoid manually constructing logit::LogRecord, filling args_array, or calling logit::Logger::get_instance() just to emit an ordinary log message. Those low-level entry points are intended for extension work such as custom ILogger / ILogFormatter implementations, backend registration, tests, benchmarks, adapters, or internal maintenance.
The asynchronous TaskExecutor supports both a mutex-protected deque and an optional lock-free MPSC ring (enable via LOGIT_USE_MPSC_RING). The same policy names (Block, DropNewest, DropOldest) are available in both implementations, but DropOldest has intentionally different semantics in MPSC mode: the deque removes the oldest accepted task, while MPSC drops the incoming task to preserve the ordering of accepted work. The ring uses LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY entries (1024 by default); adjust the baseline with that macro alongside LOGIT_SET_MAX_QUEUE(...) if your workload needs a different buffer size. MPSC builds also allow "hot" resizes where producers briefly wait while the worker rebuilds the ring without losing in-flight tasks.
Define LOGIT_SHORT_NAME before including <logit.hpp> to enable single-letter level prefixes:
For a standalone program that brings everything together and intentionally aborts after logging a fatal message, check examples/example_logit_minimal_crash.cpp.
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.
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.
Beyond the base logging calls, LogIt++ exposes canonical helper families for addressing a specific backend, applying conditions, and measuring scope duration.
For remote control surfaces, diagnostics endpoints, or embedded operator tools, LogIt++ can register a dedicated in-memory backend and expose the latest logs as either formatted strings or structured entries.
MemoryLogger snapshots are returned oldest-to-newest. The max_bytes retention budget counts buffered formatted-message payload bytes rather than the full object footprint of each BufferedLogEntry. Snapshot reads avoid the Logger execution mutex, but they still synchronize on the memory backend's own mutex while copying the current buffer.
Use ILogReader and ILogSubscriber when application code should work with either MemoryLogger or MdbxLogger. The shared macro helpers return LogRecordSnapshot records and avoid depending on backend-specific storage:
LOGIT_READ_RANGE, LOGIT_READ_RECENT_ASC, and LOGIT_READ_RECENT_DESC use ILogReader. LOGIT_ADD_LOG_CALLBACK and LOGIT_REMOVE_LOG_CALLBACK use ILogSubscriber; callbacks receive a LogRecordSnapshot after the backend has written the record. Snapshots own their string fields, so they can be copied or stored by value. Callback dispatch follows registration order. This is the preferred fallback-friendly API between MemoryLogger and MdbxLogger.
LOGIT_GET_BUFFERED_STRINGS and LOGIT_GET_BUFFERED_ENTRIES are convenience helpers for the MemoryLogger snapshot buffer. They are useful for local diagnostics panes, but code that should switch between in-memory and MDBX storage should prefer the shared LOGIT_READ_* and callback macros above.
File-based backends also expose persisted-file access through LOGIT_LIST_LOG_FILES(index), LOGIT_READ_LOG_FILE(index, path), and LOGIT_READ_LOG_FILES(index, paths). These helpers return only what has already reached disk; they do not drain async queues, and compressed rotated files are listed as metadata-only artifacts in v1. Use MemoryLogger for near-real-time snapshots and the file APIs for operational reads of persisted daily logs.
LogIt++ provides a robust and flexible set of features to accommodate various logging needs.
Customize log message formats using patterns. You can redefine patterns via macros or provide them directly when adding a logger backend. Both standard format flags (e.g., H, M, S, v) and special ones, like N([...]) for fallback logs without arguments, are supported.
Log variables and messages easily using macros. Simply select the appropriate macro and pass variables or arguments. Use LOGIT_PRINTF_<LEVEL> for printf-style formatting and LOGIT_FORMAT_<LEVEL> to apply the same format to every argument.
Canonical public macro families also include:
LOGIT_<LEVEL>_TO, LOGIT_PRINTF_<LEVEL>_TO, LOGIT_FMT_<LEVEL>_TO, and LOGIT_STREAM_<LEVEL>_TO.LOGIT_<LEVEL>_IF, LOGIT_PRINTF_<LEVEL>_IF, LOGIT_FORMAT_<LEVEL>_IF, and LOGIT_FMT_<LEVEL>_IF.LOGIT_SCOPE_<LEVEL>, LOGIT_SCOPE_PRINTF_<LEVEL>, and LOGIT_SCOPE_FMT_<LEVEL>, including their _T threshold variants.LOGIT_PERROR_<LEVEL>, LOGIT_WINERR_<LEVEL>, and LOGIT_SYSERR_<LEVEL>.LOGIT_ADD_LOGGER(...) and the built-in LOGIT_ADD_* backend macros.LOGIT_GET_*, LOGIT_SET_*, LOGIT_IS_*, LOGIT_GET_LOG_LEVEL(index), LOGIT_LIST_LOG_FILES(index), LOGIT_READ_LOG_FILE(index, path), LOGIT_READ_LOG_FILES(index, paths), LOGIT_WAIT(), LOGIT_SHUTDOWN(), LOGIT_GET_DROPPED_TASKS(), and LOGIT_RESET_DROPPED_TASKS().LOGIT_GET_BUFFERED_STRINGS(index) and LOGIT_GET_BUFFERED_ENTRIES(index) for backends that keep recent history.Pattern-consistent aliases exist for short-name and compatibility paths, but the families above are the canonical public surface to document and prefer in examples.
Easily configure loggers for output to the console, files, or system logging facilities. Optionally, add support for sending messages to servers or databases by creating custom backends.
Most general-purpose native backends handle messages asynchronously by default. Crash backends and PrometheusPayloadLogger are synchronous. OTLP HTTP and payload exporters own their queues and workers and can be configured for synchronous or asynchronous delivery. Dedicated executors create one worker per selected backend. Emscripten builds without pthreads drain cooperatively without OS worker threads.
Control how the asynchronous queue behaves when full by setting a queue policy.
Available policies: LOGIT_QUEUE_DROP_NEWEST, LOGIT_QUEUE_DROP_OLDEST, LOGIT_QUEUE_BLOCK.
Backend Config structs can set use_dedicated_executor=true to isolate a slow async sink from the global task executor. Native builds create one worker thread per configured logger; single-threaded Emscripten builds use a cooperative per-instance queue instead.
Use stream operators for complex messages.
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:
With this configuration, TRACE, DEBUG, and INFO macros are disabled at compile time.
Runtime filtering via LOGIT_SET_LOG_LEVEL(...) and LOGIT_SET_LOG_LEVEL_TO(...) still works for compiled-in severities, but it cannot re-enable macros removed earlier by LOGIT_COMPILED_LEVEL.
Create custom loggers and formatters to meet your specific requirements. See the complete implementation in \ref custom_backend_sec below; it matches the current ILogger and ILogFormatter interfaces.
Here's a simple example demonstrating how to use LogIt++ in your application:
LogIt++ supports customizable log message formatting using patterns that define the appearance of each log message. You can specify patterns either through macros or by providing them directly when adding logger backends.
You can define a custom format for the console logger as follows:
Alternatively, use macros to specify a pattern:
In both cases, the logger will automatically replace placeholders in the pattern with corresponding data, such as:
LogIt++ supports customizable log message formatting using format flags. You can define how each log message should appear by including placeholders for different pieces of information such as the timestamp, log level, file name, function name, and message.
Below is a list of all supported format flags and their meanings:
%Y: Year (e.g., 2024)%m: Month (01-12)%d: Day of the month (01-31)%H: Hour (00-23)%M: Minute (00-59)%S: Second (00-59)%e: Millisecond (000-999)%C: Two-digit year (e.g., 24 for 2024)%c: Full date and time (e.g., Mon Oct 4 12:45:30 2024)%D: Short date (e.g., 10/04/24)%T, %X: Time in ISO 8601 format (e.g., 12:45:30)%F: Date in ISO 8601 format (e.g., 2024-10-04)%s, %E: Unix timestamp in seconds%ms: Unix timestamp in milliseconds%b: Abbreviated month name (e.g., Jan)%B: Full month name (e.g., January)%a: Abbreviated weekday name (e.g., Mon)%A: Full weekday name (e.g., Monday)%l: Full log level (e.g., INFO, ERROR)%L: Short log level (e.g., I for INFO, E for ERROR)%f, %fn, %bs: Base name of the source file (e.g., main.cpp)%g, %ffn: Full file path (e.g., /home/user/project/src/main.cpp)%@: Source file and line number (e.g., main.cpp:45)%#: Line number (e.g., 45)%!: Function name (e.g., main)%t: Thread identifier%^: Start color formatting%$: End color formatting%SC: Start removing color codes (Strip Color)%EC: End removing color codes (End Color)%v: The log message content%N(...): Fallback format for cases with no arguments.%N(g:%#)), the specified sub-pattern will be applied if no arguments are provided in the log macro (e.g., LOG_TRACE0()).- before the width, e.g., %-10v (aligns text to the left).= before the width, e.g., %=10v (centers text).%10v (aligns text to the right).Truncation:
! after the width to truncate text if it exceeds the specified length, e.g., %10!v.Examples**:
%10v: Right-aligned message with a width of 10 characters.%-10v: Left-aligned message with a width of 10 characters.%10!v: Right-aligned, truncated to 10 characters.%-10!v: Left-aligned, truncated to 10 characters.For file-related flags (%f, %g, %@), truncation ensures that the filename and the beginning of the path are preserved, replacing the middle portion with ... if the width is smaller than the path length.
Example:
/very/long/path/to/file.cpp/very...file.cppLogIt++ provides shortened versions of logging macros when LOGIT_SHORT_NAME is defined. These macros allow for concise logging across different log levels, including both standard and stream-based logging.
LOG_T(...): Logs a TRACE-level message.LOG_T0(): Logs a TRACE-level message without arguments.LOG_0T(): Alias for LOG_T0().LOG_0_T(): Alias for LOG_T0().LOG_T_NOARGS(): Alias for LOG_T0().LOG_NOARGS_T(): Alias for LOG_T0().LOG_TF(fmt, ...): Logs a formatted TRACE-level message using format strings.LOG_FT(fmt, ...): Alias for LOG_TF(fmt, ...).LOG_T_PRINT(...): Logs a TRACE-level message by printing each argument.LOG_PRINT_T(...): Alias for LOG_T_PRINT(...).LOG_T_PRINTF(fmt, ...): Logs a formatted TRACE-level message using printf-style formatting.LOG_PRINTF_T(fmt, ...): Alias for LOG_T_PRINTF(fmt, ...).LOG_TP(...): Alias for LOG_T_PRINT(...).LOG_PT(...): Alias for LOG_T_PRINT(...).LOG_TPF(fmt, ...): Alias for LOG_T_PRINTF(fmt, ...).LOG_PFT(fmt, ...): Alias for LOG_T_PRINTF(fmt, ...).LOG_TRACE(...): Logs a TRACE-level message (same as LOG_T(...)).LOG_TRACE0(): Logs a TRACE-level message without arguments (same as LOG_T0()).LOG_0TRACE(): Alias for LOG_TRACE0().LOG_0_TRACE(): Alias for LOG_TRACE0().LOG_TRACE_NOARGS(): Logs a TRACE-level message with no arguments (same as LOG_T_NOARGS()).LOG_NOARGS_TRACE(): Alias for LOG_TRACE_NOARGS().LOG_TRACEF(fmt, ...): Logs a formatted TRACE-level message (same as LOG_TF(fmt, ...)).LOG_FTRACE(fmt, ...): Alias for LOG_TRACEF(fmt, ...).LOG_TRACE_PRINT(...): Logs a TRACE-level message by printing each argument (same as LOG_T_PRINT(...)).LOG_PRINT_TRACE(...): Alias for LOG_TRACE_PRINT(...).LOG_TRACE_PRINTF(fmt, ...): Logs a formatted TRACE-level message using printf-style formatting (same as LOG_T_PRINTF(fmt, ...)).LOG_PRINTF_TRACE(fmt, ...): Alias for LOG_TRACE_PRINTF(fmt, ...).These macros provide flexibility and convenience when logging messages at the TRACE level. They allow you to choose between different logging styles, such as standard logging, formatted logging, and printing each argument separately.
Note:** Similar macros are available for other log levels — INFO (LOG_I, LOG_INFO), DEBUG (LOG_D, LOG_DEBUG), WARN (LOG_W, LOG_WARN), ERROR (LOG_E, LOG_ERROR), and FATAL (LOG_F, LOG_FATAL). The naming conventions are consistent across levels; replace the level letter or word in the macro name.
LogIt++ provides several macros that allow for customization and configuration. Below are the available configuration macros:
Defines the base path used for log file paths. If LOGIT_BASE_PATH is not defined or is empty ({}), the full path from __FILE__ will be used for log file paths.
Sets the default color for console output. If LOGIT_DEFAULT_COLOR is not defined, it defaults to TextColor::LightGray.
Defines the console text color for each log level. By default, each log level is associated with a specific color, but these can be customized.
Available log levels:
TextColor::DarkGrayTextColor::BlueTextColor::GreenTextColor::YellowTextColor::RedTextColor::MagentaDefines the fallback console color used when a message does not map to a severity-specific override.
Override the wall-clock or monotonic timestamp helpers used internally by the library. This is useful when integrating platform-specific clock sources.
Macro to get the current timestamp in milliseconds. By default, it uses std::chrono for time calculation. You can override this to customize the timestamp generation.
LOGIT_CONSOLE_PATTERN is not defined, it defaults to %H:%M:%S.%e | %^N([%50!g:%#])%v%$.Defines the default directory path for log files. If LOGIT_FILE_LOGGER_PATH is not defined, it defaults to "data/logs".
Defines the number of days after which old log files are deleted. If LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS is not defined, it defaults to 30 days.
Defines the default log pattern for file-based loggers. If LOGIT_FILE_LOGGER_PATTERN is not defined, it defaults to [%Y-%m-%d %H:%M:%S.%e] [%-5l] [%60!@] [thread:%t] %SC%v.
Defines the optional size threshold for rotating file loggers. Set it to a non-zero value to enable size-based rotation.
Defines how many rotated files are retained when size-based rotation is active.
Defines the default directory path for unique log files. If LOGIT_UNIQUE_FILE_LOGGER_PATH is not defined, it defaults to "data/logs/unique_logs".
Defines the default log pattern for unique file-based loggers. If LOGIT_UNIQUE_FILE_LOGGER_PATTERN is not defined, it defaults to v.
Defines the length of the hash used in the unique log file names. If LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH is not defined, it defaults to 8 characters.
This macro controls the length of the hash part in the filenames for unique log files, ensuring unique names for each log.
Control how decoded errno / GetLastError() information is appended by the system-error macro families (LOGIT_PERROR_*, LOGIT_WINERR_*, LOGIT_SYSERR_*).
Customize how key-value tags are rendered after the main message.
Controls how frequently blocking producers poll for capacity while LOGIT_QUEUE_BLOCK is active.
Defines how many queued tasks a worker drains per iteration in ring-buffer builds before yielding.
Defines the default MPSC ring capacity used when unlimited queue mode still needs a backing ring size.
Enables short names for logging macros, such as LOG_T, LOG_D, LOG_E, etc., for concise logging.
LogIt++ allows you to extend the logging system by creating your own loggers and formatters. This section explains how to implement a custom backend for logging and a custom log formatter.
Normal application code should continue to use the public LOGIT_* / LOG_* macros for emitting messages. The low-level APIs in this section are for extension points only: custom backends, custom formatters, tests, adapters, and other infrastructure code that intentionally works below the macro layer.
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:
This FileLogger class writes log messages to a specified file. You can add this logger to your logging system like this:
Prefer LOGIT_ADD_LOGGER(...) when it fits your use case; calling logit::Logger::get_instance().add_logger(...) directly is mainly useful when an extension scenario cannot be expressed through the registration macro.
In addition to creating custom loggers, you can also create custom formatters by implementing the ILogFormatter interface. Here's an example of a simple formatter that outputs log messages in JSON format:
This JsonLogFormatter formats log messages as JSON objects. You can combine it with any logger, including the FileLogger, as shown below:
By implementing your own ILogger and ILogFormatter, you can extend the functionality of LogIt++ to log messages to various destinations or in different formats. You can combine custom loggers and formatters to create powerful logging solutions tailored to your application's needs.
Here's a quick summary:
LOGIT_ADD_LOGGER(...) for the common extension path; logit::Logger::get_instance().add_logger() is the lower-level equivalent when you need direct control.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.
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:
If you have already cloned the repository without submodules, you can initialize and update the submodules by running the following commands:
Since LogIt++ is a header-only library, you can simply include the main header in your project:
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, 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.
LogIt++ ships with the fmt library for {}-style formatting. To use the LOGIT_FMT_* and LOGIT_SCOPE_FMT_* macros, build the library with the CMake option -DLOGIT_WITH_FMT=ON.
All build-time toggles:
LOGIT_CPP_BUILD_TESTS (default: ON when this repository is the top-level project) — build the test suite.LOGIT_CPP_BUILD_EXAMPLES (default: OFF) — build the example programs.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.LOGIT_USE_MPSC_RING (default: ON) — enable the lock-free task queue instead of the mutex-backed deque.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.
Benchmark commands, methodology, historical measurements, and harness details are maintained on the dedicated \ref benchmarks page. Keeping this material in one place avoids conflicting snapshots between the API reference and the benchmark guide.
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:
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.
The LogIt++ library is open-source and hosted on GitHub: LogIt++ GitHub Repository.
This library is licensed under the MIT License. See the LICENSE file in the repository for more details.