Thread-safe C++ logging library with console and file output and a configurable time format.
logcoe is a small thread-safe C++ logging library. It writes to the console, to a file or to both:
[06/07/2025__14:30:25] [INFO]: Application started successfully
[06/07/2025__14:30:25] [DEBUG] [NetworkManager]: Connecting to server 192.168.1.100
[06/07/2025__14:30:26] [WARNING] [Database]: Connection timeout, retrying...
[06/07/2025__14:30:27] [ERROR] [FileSystem]: Failed to open configuration file
No third-party libraries. Building the tests fetches testcoe from source. Toolchain versions are under Requirements.
include(FetchContent)
FetchContent_Declare(
logcoe
GIT_REPOSITORY https://github.com/nircoe/logcoe.git
GIT_TAG v0.1.1
)
FetchContent_MakeAvailable(logcoe)
target_link_libraries(your_target PRIVATE logcoe)Linking logcoe turns on C++23 for your_target, so you don't have to set CMAKE_CXX_STANDARD yourself.
#include <logcoe.hpp>
int main()
{
// Initialize with INFO level, no default source (empty string), console enabled, file disabled
// logs will be printed as [timestamp] [log_level]: <log_message>
logcoe::initialize(logcoe::log_level::info, std::string{}, true, false);
logcoe::info("Application started");
logcoe::infof("Loaded {} files in {} ms", 12, 340);
logcoe::warning("This is a warning message");
logcoe::error("Critical error occurred", "ErrorHandler");
logcoe::shutdown();
return 0;
}#include <logcoe.hpp>
#include <fstream>
int main()
{
// Enable both console and file output with DEBUG level and default source as logcoe
// logs will be printed as [timestamp] [log_level] [logcoe]: <log_message>
logcoe::initialize(logcoe::log_level::debug, "logcoe", true, true, "app.log");
// Customize time format
if (!logcoe::set_time_format("%H:%M:%S"))
return 1;
// Log with source information
logcoe::debug("Debugging network connection", "NetworkModule");
logcoe::info("User logged in successfully", "AuthSystem");
// Redirect console to custom stream
std::ofstream custom_log("custom.log");
logcoe::set_console_output(custom_log);
// Change log level at runtime
logcoe::set_log_level(logcoe::log_level::warning);
logcoe::shutdown();
return 0;
}- Thread-safe, one mutex guards every call
- Log levels debug, info, warning and error, filtered at runtime
- Console and file output at the same time
- Log level, console stream, log file and time format can change at runtime
- One public header, built as a static library
- Windows, Linux and macOS
- Compiles to no-op stubs in Release builds, see Release Builds
All functions are declared in include/logcoe.hpp, with their default arguments.
initializecan be called more than once. Later calls ignore their arguments and only raise a counter. Logging stops after the matching number ofshutdowncalls.- The logging functions and the setters do nothing before
initialize. - The logging functions take an optional
sourceand aflushflag (defaulttrue). A message logged withflush==falsemay stay in the stream buffer until the nextlogcoe::flush()orshutdown().logcoe::flush()flushes the console and file outputs. debugf,infof,warningfanderrorftake astd::formatstring and arguments.std::format_stringchecks the format string at compile time, so a bad one does not build. A message below the log level is not formatted. They use the default source andflush==true. To pass asourceorflush, format the message yourself and call the plain function. Astd::formatterthat throws propagates out of the call.- The public header includes
<format>. set_file_outputandset_time_formatreturnstd::expected<void, logcoe::error_reason>.set_file_output,set_time_format,is_initializedandget_log_levelare[[nodiscard]].error_reasonisfile_open_failure,invalid_time_formatornot_initialized. Both functions returnnot_initializedwhen called beforeinitialize.- Time formats use
strftimesyntax.
| Level | Value | Description |
|---|---|---|
debug |
0 | Detailed diagnostic information |
info |
1 | General application information |
warning |
2 | Warning conditions that should be noted |
error |
3 | Error conditions that affect functionality |
none |
4 | Disable all logging |
All functions can be called from any thread. A single mutex is held for the whole of each call, see Architecture.
- Compiler: C++23 with
<expected>and<format>(GCC 13+, Clang 17+ with libc++, Apple Clang from Xcode 15.3+, MSVC 2022 17.3+) - Build system: CMake 3.22+
- Platforms: Windows, Linux, macOS
Defining NDEBUG (for example -DCMAKE_BUILD_TYPE=Release) replaces every function with a no-op stub at
compile time. Call sites compile unchanged, but nothing is logged, formatted or written to a file:
initialize,debug/info/warning/error,flush,shutdownand the other setters do nothing.debugf/infof/warningf/errorfreturn early becauseget_log_level()isnone, so they do not format.is_initialized()returnsfalse.get_log_level()returnslog_level::none.set_file_output(...)returns success and never creates a file.set_time_format(...)returns success.
This is automatic whenever NDEBUG is defined, no option is needed. It includes RelWithDebInfo and
MinSizeRel, because CMake defines NDEBUG for those build types too.
- Architecture: how it works
- Contributing: build, test and PR rules
- Roadmap: version history and planned features
MIT License, see LICENSE.