logme is a cross-platform C/C++ logging framework that allows applications to dynamically control what is logged and where, without restarting or recompiling.
It is designed for both high-load servers and simple applications, providing selective, on-demand logging with minimal overhead and full runtime control.
- 📚 Documentation (Wiki): https://github.com/efmsoft/logme/wiki
- 💬 Support / feedback: GitHub issues and discussions are welcome.
- 🧭 Feature discovery map: docs/feature_discovery.md maps common logging-library terms such as null sink, rate limiting, duplicate suppression, rotation, and retention to the corresponding logme mechanisms.
logme is designed as a logging infrastructure layer, not just a set of formatting macros. Its core model is built around runtime control: channels, subsystems, links, backends, output flags, and configuration can be changed while the application is running. This makes it useful for production systems where detailed diagnostics must be enabled only when needed and only for the affected component.
The project also includes companion tools and operational features: logmectl and logmeweb for runtime control, policy-aware control commands, optional startup control from environment variables, logmefmt for converting readable logme output to JSON/XML, logmeobf for log obfuscation, file rotation and retention, structured output, recent-history capture with RingBufferBackend, function tracing/profiling macros, and early/boot logging support.
For the design background, see the logging articles at tips.efmsoft.com.
Performance is one of logme's main design goals, especially when logging is disabled, filtered out, or routed to asynchronous output. The separate logbench repository contains a reproducible benchmark suite and an article with charts and methodology.
Recent throughput results, measured as completed cycles during the same benchmark interval:
| Library | null | file | console | file+console |
|---|---|---|---|---|
| logme (c) | 1,121,998,419 | 153,752,170 | 325,518 | 359,772 |
| logme (cpp-stream) | 1,053,074,266 | 24,290,646 | 335,276 | 364,349 |
| logme (std::format) | 1,082,512,863 | 90,255,191 | 362,369 | 365,770 |
| spdlog | 244,991,156 | 118,546,708 | 368,944 | 355,449 |
| quill | 1,568,610 | 1,357,532 | 191,531 | 200,378 |
| easylogging++ | 25,597,177 | 1,656,726 | 356,948 | 277,846 |
The result is not based on a single trick. logme combines early filtering, call-site context caching, low-cost disabled paths, multiple formatting APIs, asynchronous file output, and runtime routing designed to avoid doing unnecessary work in hot paths.
See the wiki pages Performance and Why logme is fast for details.
- Runtime control via a built-in control server: enable, disable, and reconfigure logging dynamically without restarting or recompiling the application.
- Policy-aware control API: execute control commands with full, safe, diagnostic, or custom permissions when commands come from less-trusted sources.
- Startup environment control: explicitly opt in to
LOGME_CONTROL/LOGME_CONTROL_Ncommands for startup diagnostics without making environment variables active by default. - Channels and subsystems: logically separate log output by component, module, or functional area.
- Channel links and routing chains: redirect or fan-out log messages between channels without treating channels as inherited parent/child configuration trees.
- Flexible verbosity control: fine-grained filtering using log levels (Debug / Info / Warn / Error), subsystems, trace points, and channel state.
- Multiple backends per channel: console, debugger, file, shared file, buffer, ring buffer, callback, and Windows Event Log destinations.
- Recent-history capture: keep the last N formatted records in memory with
RingBufferBackendand dump them only when needed. - Retention rules: limit the size of individual log files and the total disk usage across all logs.
- Log file obfuscation: optional obfuscation of log data written to files.
- Multiple APIs: C-style macros, C++ stream-style logging, and optional
format-based formatting. - Cross-platform: Windows, Linux and macOS support.
The simplest possible usage. This works out of the box and requires no configuration.
#include <Logme/Logme.h>
int main()
{
LogmeI("Hello from logme (%s style)", "C");
LogmeW() << "Hello from logme (C++ stream style)";
fLogmeE("Hello from logme ({} style)", "format");
return 0;
}This produces colored console output similar to the following:
For most applications, logme is configured via a JSON configuration file, which can describe the following elements:
- channels
- backends
- links
- levels
- flags
- subsystems
This lets you adjust logging behavior at runtime or between runs without recompiling.
For details and examples, see the project Wiki.
All aspects of logme behavior can be configured directly from C++ application code. This approach is useful for small tools, embedded scenarios, or when configuration files are not desired.
#include <memory>
#include <Logme/Logme.h>
#include <Logme/Backend/ConsoleBackend.h>
int main()
{
auto ch = Logme::Instance->CreateChannel("http");
ch->AddBackend(std::make_shared<Logme::ConsoleBackend>(ch));
fLogmeI(ch, "GET {} -> {}", "/index.html", 200); // -> GET /index.html -> 200
return 0;
}Applications using logme can optionally enable a built-in control server that allows logging behavior to be fully managed at runtime.
The control server exposes a management interface for:
- creating and removing channels,
- attaching or detaching backends,
- enabling, disabling, or blocking channels,
- controlling subsystems, levels, and routing.
A command-line utility is provided with the library to send control commands to a running process, and logmeweb can provide browser-based runtime control. The specific tool is not important — the key point is that logging can be reconfigured dynamically while the application is running.
The same command language can also be used at startup from environment variables, but only when the application explicitly calls ApplyEnvironmentControl(). This keeps environment variables inactive by default and lets the application pass a ControlPolicy that limits which commands are accepted. For example:
LOGME_CONTROL="level --channel raw debug; trace enable Raw:*:*"A common use case is a modular application where each component writes to its own channel.
At startup:
- channels may not exist at all,
- messages sent to non-existent or inactive channels are silently dropped,
- no log files are created,
- no console output is produced.
When diagnostics are required:
- a channel can be created remotely,
- backends can be attached (file, console, debugger),
- messages from the selected component immediately start appearing.
Later:
- the channel can be removed,
- blocked,
- or detached from all backends,
- instantly stopping further output.
This model allows selective, on-demand logging:
- only for specific components,
- only when needed,
- only to selected destinations.
It keeps production systems quiet by default while still allowing deep, targeted diagnostics without restarting the application or recompiling it.
A channel is where you write messages. A channel can have:
- one or more backends (output destinations),
- optional links to other channels (redirect / fan-out).
A backend defines where messages go (console, debugger, file, etc.). Multiple backends can be attached to the same channel.
A subsystem is an optional identifier that provides an additional level of classification inside a channel. Subsystems allow grouping and filtering messages within the same channel, making it possible to enable or disable logging for specific functional parts of a component without affecting the entire channel.
A link forwards messages from one channel to another after local backends are processed.
This is useful for building routing trees (e.g., http → root).
include(FetchContent)
FetchContent_Declare(
logme
GIT_REPOSITORY https://github.com/efmsoft/logme.git
GIT_TAG main # Prefer a release tag when available
)
FetchContent_MakeAvailable(logme)
target_link_libraries(your_target PRIVATE logme)add_subdirectory(external/logme)
target_link_libraries(your_target PRIVATE logme)An example of integrating logme as a git submodule can be found here: https://github.com/efmsoft/logme_cmake_submodule_example
vcpkg install logmefind_package(logme CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE logme::logme)conan install --requires=logme/<version>find_package(logme REQUIRED)
target_link_libraries(your_target PRIVATE logme::logme)cmake -S . -B build
cmake --build buildcmake -S . -B build -DLOGME_BUILD_TESTS=ON
cmake --build build
ctest --test-dir buildLOGME_BUILD_EXAMPLES(ON/OFF)LOGME_BUILD_TESTS(ON/OFF)LOGME_BUILD_STATIC(ON/OFF)LOGME_BUILD_DYNAMIC(ON/OFF)LOGME_BUILD_TOOLS(ON/OFF)LOGME_ENABLE_INSTALL(ON/OFF)USE_JSONCPP(AUTO,ON,OFF)LOGME_FMT_FORMAT(AUTO,ON,OFF)LOGME_STD_FORMAT(AUTO,ON,OFF)
- C++20 is used.
format is optional. If your standard library does not provide <format>, disable it via:
-DLOGME_FMT_FORMAT=OFF -DLOGME_STD_FORMAT=OFF(or defineLOGME_DISABLE_STD_FORMAT)
- More production backends, especially syslog and systemd-journal integration
- Richer file lifecycle policies such as compression and additional rotation/retention modes
- Backend-level filtering options for selected advanced routing scenarios
- More documentation, articles, and integration examples
Contributions are welcome via issues and pull requests.
Please include:
- OS and compiler version,
- CMake options used,
- minimal reproduction steps if applicable.
See the LICENSE file.


