LogIt++
Header-only, macro-first C++ logging library
Loading...
Searching...
No Matches
API reference and concepts

Overview

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_<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.
  • 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.

Macro-first usage

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.

Backpressure and queue variants

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.

Macro Examples

Long-form macros

#include <logit.hpp>
int main() {
const bool verbose = true;
int attempt = 1;
double latency_ms = 12.5;
LOGIT_DEBUG_IF(verbose, "Verbose diagnostics enabled");
LOGIT_INFO("Starting service", attempt);
LOGIT_WARN_ONCE("initializing subsystem");
LOGIT_ERROR_EVERY_N(3, "retrying connection", attempt);
LOGIT_ERROR_THROTTLE(250, "still failing");
LOGIT_PRINTF_WARN("Latency %.2f ms", latency_ms);
LOGIT_FORMAT_INFO("%.2f", 1.23f, 4.56f);
LOGIT_INFO_TAG(({{"order_id", 123}, {"side", "BUY"}}), "sent order");
LOGIT_STREAM_INFO() << "Streaming value: " << attempt;
}
int main()
#define LOGIT_ERROR_THROTTLE(p,...)
#define LOGIT_ADD_CONSOLE_DEFAULT()
Macro for adding the default console logger.
#define LOGIT_TRACE0()
#define LOGIT_PRINTF_WARN(fmt,...)
#define LOGIT_WAIT()
Macro for waiting for all asynchronous loggers to finish processing.
#define LOGIT_INFO(...)
#define LOGIT_SET_QUEUE_POLICY(mode)
Sets the behavior when the queue is full.
#define LOGIT_FORMAT_INFO(fmt,...)
#define LOGIT_INFO_TAG(msg,...)
#define LOGIT_WARN_ONCE(...)
#define LOGIT_SET_MAX_QUEUE(size)
Sets the maximum number of queued tasks.
#define LOGIT_DEBUG_IF(condition,...)
#define LOGIT_QUEUE_DROP
Backward-compatible alias for dropping the newest task.
#define LOGIT_ERROR_EVERY_N(n,...)
#define LOGIT_STREAM_INFO()
Unified umbrella header for the LogIt++ library.

Short aliases

Define LOGIT_SHORT_NAME before including <logit.hpp> to enable single-letter level prefixes:

#define LOGIT_SHORT_NAME
#include <logit.hpp>
void short_names_demo() {
LOGIT_ADD_CONSOLE_DEFAULT(); // call once during initialization
int attempt = 2;
LOG_I("Short alias for info");
LOG_IPF("Attempt %d finished", attempt);
LOG_W("Warning alias");
LOG_WPF("Retry %d/3", attempt);
LOG_S_INFO() << "Streaming alias " << attempt;
}

For a standalone program that brings everything together and intentionally aborts after logging a fatal message, check examples/example_logit_minimal_crash.cpp.

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.

#include <logit.hpp>
void handle_request() {
LOGIT_MDC_PUT("request_id", "req-42");
{
LOGIT_NDC_GUARD("checkout");
LOGIT_INFO("processing order");
}
}
#define LOGIT_MDC_PUT(key, value)
#define LOGIT_NDC_GUARD(value)
#define LOGIT_MDC_CLEAR()

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.

Targeted, conditional, and scope helpers

Beyond the base logging calls, LogIt++ exposes canonical helper families for addressing a specific backend, applying conditions, and measuring scope duration.

#include <logit.hpp>
void process_request(bool verbose, double latency_ms) {
LOGIT_INFO_TO(1, "Only logger index 1 receives this message");
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);
}
#define LOGIT_SCOPE_PRINTF_WARN_T(threshold_ms, fmt_str,...)
#define LOGIT_INFO_TO(index,...)
#define LOGIT_PRINTF_INFO_IF(condition, fmt,...)
#define LOGIT_ADD_UNIQUE_FILE_LOGGER_DEFAULT_SINGLE_MODE()
Macro for adding the default unique file logger in single_mode.
#define LOGIT_SCOPE_INFO(phase)

In-memory snapshot logger

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.

#include <logit.hpp>
int main() {
LOGIT_ADD_MEMORY_LOGGER_SINGLE_MODE(1000, 1024 * 1024, 24LL * 60 * 60 * 1000); // index 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;
}
#define LOGIT_GET_BUFFERED_ENTRIES(logger_index)
Retrieves buffered structured entries from a specific logger.
#define LOGIT_GET_LOG_LEVEL(logger_index)
Retrieves minimal log level for a specific logger.
#define LOGIT_ADD_MEMORY_LOGGER_SINGLE_MODE(max_records, max_bytes, max_age_ms)
Macro for adding an in-memory logger in single_mode with explicit limits.
#define LOGIT_WARN_TO(index,...)
#define LOGIT_GET_BUFFERED_STRINGS(logger_index)
Retrieves buffered formatted messages from a specific logger.

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.

Common stored-log API

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:

#include <logit.hpp>
int main() {
// This can be a MemoryLogger index or an MdbxLogger index.
const int backend_index = 0;
const int64_t now_ms = LOGIT_CURRENT_TIMESTAMP_MS();
const auto recent = LOGIT_READ_RECENT_ASC(backend_index, 100, 0);
const auto window = LOGIT_READ_RANGE(
backend_index,
now_ms - 60LL * 60 * 1000,
now_ms + 1,
0);
std::vector<logit::LogRecordSnapshot> live_updates;
const uint64_t callback_id = LOGIT_ADD_LOG_CALLBACK(
backend_index,
([&live_updates](const logit::LogRecordSnapshot& record) {
live_updates.push_back(record);
}));
LOGIT_INFO_TO(backend_index, "visible through read and callback APIs");
LOGIT_REMOVE_LOG_CALLBACK(backend_index, callback_id);
(void)recent;
(void)window;
(void)live_updates;
}
#define LOGIT_CURRENT_TIMESTAMP_MS()
Macro to get the current timestamp in milliseconds.
Definition config.hpp:84
#define LOGIT_ADD_LOG_CALLBACK(logger_index, callback)
Registers a callback to receive newly written log records.
#define LOGIT_READ_RECENT_ASC(logger_index, limit, period_ms)
Reads recent records in ascending order (oldest first).
#define LOGIT_REMOVE_LOG_CALLBACK(logger_index, callback_id)
Unregisters a previously added log callback.
#define LOGIT_READ_RANGE(logger_index, from_ms, to_ms, limit)
Reads a time-range of records from a backend that supports ILogReader.
Common owning snapshot of a stored log record.

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.

Features

LogIt++ provides a robust and flexible set of features to accommodate various logging needs.

Flexible Log Formatting

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.

#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) {
}
// Output:
> 23:59:59.128 | An example runtime error
#define LOGIT_FATAL(...)

Logging with Macros

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.

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-style
LOGIT_FORMAT_INFO("%.2f", someFloat, 654.321f); // same format for all args
#define LOGIT_PRINTF_INFO(fmt,...)
#define LOGIT_PRINT_INFO(...)

Canonical public macro families also include:

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.

Support for Multiple Backends

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.

// Adding several backends: console, file, unique file and system loggers
#define LOGIT_ADD_FILE_LOGGER_DEFAULT()
Macro for adding the default file logger.
#define LOGIT_ADD_SYSLOG_DEFAULT()
Macro for adding a syslog logger with default configuration.
#define LOGIT_ADD_EVENT_LOG_DEFAULT()
Macro for adding a Windows Event Log logger with default configuration.

Asynchronous Logging

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.

Queue Buffer Modes

Control how the asynchronous queue behaves when full by setting a queue policy.

#define LOGIT_QUEUE_BLOCK
Queue policy for blocking when the queue is full.

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.

cfg.async = true;
cfg.queue_capacity = 1024;
cfg.queue_policy = logit::QueuePolicy::Block;
(cfg),
);
1024,
logit::QueuePolicy::DropNewest
);
Outputs log messages to the console with optional ANSI color support.
A simple log formatter that formats log messages based on a user-defined pattern.
#define LOGIT_CONSOLE_PATTERN
Defines the default log pattern for the console logger.
Definition config.hpp:96
#define LOGIT_ADD_CONSOLE_DEDICATED(pattern, queue_capacity, queue_policy)
Add an async console logger backed by a dedicated executor.
#define LOGIT_ADD_CONSOLE_CONFIG(config, pattern)
Add a console logger from an explicit ConsoleLogger::Config.
#define LOGIT_ADD_LOGGER(logger_type, logger_args, formatter_type, formatter_args)
Macro for adding a logger with a specific formatter.
Configuration for the console logger.
bool async
Flag indicating whether logging should be asynchronous.
QueuePolicy queue_policy
Overflow policy for the dedicated executor.
std::size_t queue_capacity
Maximum queue size for the dedicated executor (0 = unlimited).
bool use_dedicated_executor
Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread p...

Stream-Based Logging

Use stream operators for complex messages.

LOGIT_STREAM_INFO() << "Stream-based info logging with short macro. Integer value: " << 123;

Compile-Time Log Level

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:

g++ -DLOGIT_COMPILED_LEVEL=LOGIT_LEVEL_WARN ...

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.

Extensibility

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.

Usage

Here's a simple example demonstrating how to use LogIt++ in your application:

#define LOGIT_SHORT_NAME
#include <logit.hpp>
int main() {
// Initialize the logger with default console output
float a = 123.456f;
int b = 789;
const char* someStr = "Hello, World!";
// Basic logging using short macros
LOG_I("Starting the application");
LOG_D(a, b);
LOG_W("This is a warning message");
// Formatted logging using short macros
LOG_IPF("Formatted log: value of a = %.2f", a);
LOG_WPF("Warning! Values: a = %.2f, b = %d", a, b);
// Error and fatal logs using short macros
LOG_EP("An error occurred with value b =", b);
LOG_F("Fatal error. Terminating application.");
// Conditional logging
LOGIT_INFO_IF(b < 0, "Value of b is negative");
LOGIT_WARN_IF(a > 100, "Value of a exceeds 100");
// Stream-based logging with short and long names
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;
// Using LOGIT_TRACE for tracing function execution
LOG_TRACE0(); // Trace without arguments
LOG_T("Entering main function with variable", a);
// Wait for all asynchronous logs to be processed
return 0;
}
#define LOGIT_INFO_IF(condition,...)
#define LOGIT_STREAM_WARN()
#define LOGIT_WARN_IF(condition,...)

Customizing Log Formats

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.

Examples of Formatting Patterns

Example for Setting a Custom Console Logger Format

You can define a custom format for the console logger as follows:

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

Example Using Macros for Simplicity

Alternatively, use macros to specify a pattern:

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

In both cases, the logger will automatically replace placeholders in the pattern with corresponding data, such as:

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

Log Message Formatting Flags

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:

Date and Time Flags

  • %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

Weekday and Month Names

  • %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)

Log Level

  • %l: Full log level (e.g., INFO, ERROR)
  • %L: Short log level (e.g., I for INFO, E for ERROR)

File and Function Information

  • %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)

Thread Information

  • %t: Thread identifier

Color Formatting

  • %^: Start color formatting
  • %$: End color formatting
  • %SC: Start removing color codes (Strip Color)
  • %EC: End removing color codes (End Color)

Message Content

  • %v: The log message content
  • %N(...): Fallback format for cases with no arguments.
    • When used in a pattern (e.g., %N(g:%#)), the specified sub-pattern will be applied if no arguments are provided in the log macro (e.g., LOG_TRACE0()).

Alignment and Truncation

  • Alignment:
    • Left: Use - before the width, e.g., %-10v (aligns text to the left).
    • Center: Use = before the width, e.g., %=10v (centers text).
    • Right (default): %10v (aligns text to the right).
  • Truncation:

    • Use ! 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.

Advanced Path Handling

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:

  • Input: /very/long/path/to/file.cpp
  • Truncated to width=15: /very...file.cpp

Shortened Logging Macros

LogIt++ 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.

Available TRACE-level macros:

  • Basic 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().
  • Formatted logging:
    • 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, ...).
  • Alternative TRACE-level macros:
    • 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.

LOG_T("Trace message using short macro");
LOG_FT("%.4d", 999);
LOG_PRINT_T("Printing trace message with multiple variables: ", var1, var2);
LOG_TRACE("Trace message (alias for LOG_T)");
LOG_PRINTF_TRACE("Formatted trace: value = %d", value);

Configuration Macros

LogIt++ provides several macros that allow for customization and configuration. Below are the available configuration macros:

  • LOGIT_BASE_PATH:

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.

// Defines the base path for project folder.
#define LOGIT_BASE_PATH "/path/to/your/project"
  • LOGIT_DEFAULT_COLOR:

Sets the default color for console output. If LOGIT_DEFAULT_COLOR is not defined, it defaults to TextColor::LightGray.

// Sets the default log message color to green.
#define LOGIT_DEFAULT_COLOR TextColor::Green
  • LOGIT_COLOR_<LOG_LEVEL>:

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:

  • TRACE: Defaults to TextColor::DarkGray
  • DEBUG: Defaults to TextColor::Blue
  • INFO: Defaults to TextColor::Green
  • WARN: Defaults to TextColor::Yellow
  • ERROR: Defaults to TextColor::Red
  • FATAL: Defaults to TextColor::Magenta
// Customize the color for different log levels
#define LOGIT_COLOR_TRACE TextColor::Blue
#define LOGIT_COLOR_ERROR TextColor::Cyan
  • LOGIT_COLOR_DEFAULT:

Defines the fallback console color used when a message does not map to a severity-specific override.

#define LOGIT_COLOR_DEFAULT TextColor::White
  • LOGIT_WALLCLOCK_MS / LOGIT_MONOTONIC_MS:

Override the wall-clock or monotonic timestamp helpers used internally by the library. This is useful when integrating platform-specific clock sources.

#define LOGIT_WALLCLOCK_MS() my_wallclock_ms()
#define LOGIT_MONOTONIC_MS() my_monotonic_ms()
  • LOGIT_CURRENT_TIMESTAMP_MS:

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.

// Customize timestamp calculation if needed.
#define LOGIT_CURRENT_TIMESTAMP_MS() my_custom_timestamp_function()
  • LOGIT_CONSOLE_PATTERN: Defines the default log pattern for the console logger. If LOGIT_CONSOLE_PATTERN is not defined, it defaults to %H:%M:%S.%e | %^N([%50!g:%#])%v%$.
// Customize the console log message pattern.
#define LOGIT_CONSOLE_PATTERN "%H:%M:%S.%e | %v"
  • LOGIT_FILE_LOGGER_PATH:

Defines the default directory path for log files. If LOGIT_FILE_LOGGER_PATH is not defined, it defaults to "data/logs".

// Specify a custom path for log files.
#define LOGIT_FILE_LOGGER_PATH "/custom/log/directory"
  • LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS:

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.

// Set the number of days to keep log files.
#define LOGIT_FILE_LOGGER_AUTO_DELETE_DAYS 60
  • LOGIT_FILE_LOGGER_PATTERN:

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.

// Customize the log file message pattern.
#define LOGIT_FILE_LOGGER_PATTERN "[%Y-%m-%d %H:%M:%S.%e] [%l] %v"
  • LOGIT_FILE_LOGGER_MAX_FILE_SIZE_BYTES:

Defines the optional size threshold for rotating file loggers. Set it to a non-zero value to enable size-based rotation.

#define LOGIT_FILE_LOGGER_MAX_FILE_SIZE_BYTES (10 * 1024 * 1024)
  • LOGIT_FILE_LOGGER_MAX_ROTATED_FILES:

Defines how many rotated files are retained when size-based rotation is active.

#define LOGIT_FILE_LOGGER_MAX_ROTATED_FILES 5
  • LOGIT_UNIQUE_FILE_LOGGER_PATH:

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".

// Specify a custom path for unique log files.
#define LOGIT_UNIQUE_FILE_LOGGER_PATH "/custom/unique/log/directory"
  • LOGIT_UNIQUE_FILE_LOGGER_PATTERN:

Defines the default log pattern for unique file-based loggers. If LOGIT_UNIQUE_FILE_LOGGER_PATTERN is not defined, it defaults to v.

// Customize the unique file log message pattern.
#define LOGIT_UNIQUE_FILE_LOGGER_PATTERN "[%Y-%m-%d %H:%M:%S.%e] [%l] %v"
  • LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH:

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.

// Set the hash length to 12 characters for unique file names.
#define LOGIT_UNIQUE_FILE_LOGGER_HASH_LENGTH 12
  • LOGIT_OS_ERROR_JOIN, LOGIT_POSIX_ERROR_PATTERN, LOGIT_WINDOWS_ERROR_PATTERN**, LOGIT_SYSTEM_ERROR_PATTERN:

Control how decoded errno / GetLastError() information is appended by the system-error macro families (LOGIT_PERROR_*, LOGIT_WINERR_*, LOGIT_SYSERR_*).

#define LOGIT_OS_ERROR_JOIN " <- "
#define LOGIT_SYSTEM_ERROR_PATTERN "%s <- code=%d (%s)"
  • LOGIT_TAGS_JOIN, LOGIT_TAG_PAIR_SEP, LOGIT_TAG_KV_SEP, LOGIT_TAG_QUOTE_VALUES**:

Customize how key-value tags are rendered after the main message.

#define LOGIT_TAGS_JOIN " || "
#define LOGIT_TAG_PAIR_SEP "; "
#define LOGIT_TAG_KV_SEP ":"
#define LOGIT_TAG_QUOTE_VALUES 0
  • LOGIT_TASK_EXECUTOR_BLOCK_WAIT_USEC:

Controls how frequently blocking producers poll for capacity while LOGIT_QUEUE_BLOCK is active.

#define LOGIT_TASK_EXECUTOR_BLOCK_WAIT_USEC 200
  • LOGIT_TASK_EXECUTOR_DRAIN_BUDGET:

Defines how many queued tasks a worker drains per iteration in ring-buffer builds before yielding.

#define LOGIT_TASK_EXECUTOR_DRAIN_BUDGET 2048
  • LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY:

Defines the default MPSC ring capacity used when unlimited queue mode still needs a backing ring size.

#define LOGIT_TASK_EXECUTOR_DEFAULT_RING_CAPACITY 1024
  • LOGIT_SHORT_NAME:

Enables short names for logging macros, such as LOG_T, LOG_D, LOG_E, etc., for concise logging.

// Enable short macro names for logging.
#define LOGIT_SHORT_NAME

Custom Logger Backend and Formatter

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.

Custom Logger Example

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:

#include <fstream>
#include <mutex>
#include <logit.hpp>
class FileLogger : public logit::ILogger {
public:
// Constructor to initialize the file logger with a file name
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();
}
}
// Logs the message to the file
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;
}
}
// Waits for any asynchronous log operations (none in this case)
void wait() override {
// 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;
std::ofstream m_log_file;
std::mutex m_mutex;
};
Optional buffered snapshot access returns empty results by default.
Definition ILogger.hpp:47
virtual double get_float_param(const LoggerParam &param) const =0
Retrieves a floating-point parameter from the logger.
virtual void set_log_level(LogLevel level)=0
Sets the minimal log level for this logger.
virtual int64_t get_int_param(const LoggerParam &param) const =0
Retrieves an integer parameter from the logger.
virtual void log(const LogRecord &record, const std::string &message)=0
Logs a message.
virtual std::string get_string_param(const LoggerParam &param) const =0
Retrieves a string parameter from the logger.
virtual void wait()=0
Waits for all asynchronous logging operations to complete.
virtual LogLevel get_log_level() const =0
Gets the minimal log level for this logger.
LogLevel
Logging levels.
Definition enums.hpp:15
@ LOG_LVL_TRACE
Trace level logging.
LoggerParam
Enumeration for different logger parameters that can be retrieved.
Definition enums.hpp:47
Stores log metadata and content.
Definition LogRecord.hpp:17

This FileLogger class writes log messages to a specified file. You can add this logger to your logging system like this:

FileLogger, ("logfile.txt"),
// or, if you intentionally need the lower-level equivalent...
std::unique_ptr<logit::ILogger>(new FileLogger("logfile.txt")),
std::unique_ptr<logit::ILogFormatter>(new logit::SimpleLogFormatter()));
void add_logger(std::unique_ptr< ILogger > logger, std::unique_ptr< ILogFormatter > formatter, bool single_mode=false)
Adds a logger and its corresponding formatter.
Definition Logger.hpp:64
static Logger & get_instance()
Retrieves singleton instance of Logger.
Definition Logger.hpp:53

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.

Custom Formatter Example

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:

#include <logit.hpp>
#include <json/json.h> // Or your preferred JSON library
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;
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["format"] = record.format;
Json::StreamWriterBuilder writer;
return Json::writeString(writer, log_entry);
}
};
Interface for formatting log records.
virtual std::string format(const LogRecord &record) const =0
Formats a log record into a string.
virtual void set_timestamp_offset(int64_t offset_ms)=0
Sets the timestamp offset for log formatting.
const std::string function
Function name.
Definition LogRecord.hpp:22
const int line
Line number in the source file.
Definition LogRecord.hpp:21
const LogLevel log_level
Log level (severity).
Definition LogRecord.hpp:18
const int64_t timestamp_ms
Timestamp in milliseconds.
Definition LogRecord.hpp:19
const std::string file
Source file name.
Definition LogRecord.hpp:20
const std::string format
Format string for the message.
Definition LogRecord.hpp:23

This JsonLogFormatter formats log messages as JSON objects. You can combine it with any logger, including the FileLogger, as shown below:

FileLogger, ("logfile.json"),
JsonLogFormatter, ());
// or, if you intentionally need the lower-level equivalent...
std::unique_ptr<logit::ILogger>(new FileLogger("logfile.json")),
std::unique_ptr<logit::ILogFormatter>(new JsonLogFormatter()));

Summary

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:

  • ILogger: Defines where the logs should be sent (e.g., file, console, database, network).
  • ILogFormatter: Defines how the logs should be formatted (e.g., plain text, JSON, XML).
  • Registering custom backends: Prefer LOGIT_ADD_LOGGER(...) for the common extension path; logit::Logger::get_instance().add_logger() is the lower-level equivalent when you need direct control.

Installation

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.

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, zstd, kurlyk, or mdbx-containers**.

To clone the repository with submodules, use the following command:

git clone --recurse-submodules https://github.com/LimiNode/log-it-cpp.git

If you have already cloned the repository without submodules, you can initialize and update the submodules by running the following commands:

git submodule init
git submodule update

Step 2: Include the LogIt++ Headers in Your Project

Since LogIt++ is a header-only library, you can simply include the main header in your project:

#include <logit.hpp>

This will give you access to the entire logging system.

Step 3: Configure Dependencies

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.

Step 4: Using fmt (Optional)

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.

CMake options

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.

Benchmarks

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.

Step 5: Build and Run Your Project

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:

find_package(log-it-cpp CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE log-it-cpp::log-it-cpp)

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.

Repository

The LogIt++ library is open-source and hosted on GitHub: LogIt++ GitHub Repository.

License

This library is licensed under the MIT License. See the LICENSE file in the repository for more details.