This tutorial walks through the fundamental usage of logger_system, covering logger construction, writer attachment, log levels, and structured logging. By the end you will be comfortable wiring up a logger that writes to console and file destinations.
Prerequisites
- C++20 capable compiler (GCC 11+, Clang 14+, MSVC 2022+, Apple Clang 14+)
- logger_system installed via CMake
FetchContent, vcpkg, or as a sibling source tree
- A linked target depending on
kcenon::logger
The complete runnable source for the patterns shown here lives in basic_usage.cpp.
Step 1: Create a Logger
The simplest way to obtain a logger is to construct one directly. Pass true to enable asynchronous mode (the default) or false for fully synchronous behaviour.
#include <kcenon/common/interfaces/logger_interface.h>
namespace ci = kcenon::common::interfaces;
using log_level = ci::log_level;
auto log = std::make_shared<logger>(true);
log->add_writer(std::make_unique<console_writer>());
log->start();
log->log(log_level::info, std::string("Application started"));
log->stop();
return 0;
}
Core console writer for logging to stdout/stderr.
Main logger implementation providing high-performance logging facilities.
Console writer for logging to stdout/stderr.
High-performance, thread-safe logging system with asynchronous capabilities.
Asynchronous mode is recommended for almost all production scenarios because the calling thread enqueues messages and returns quickly. Synchronous mode is useful for unit tests or short-lived utilities where determinism is more important than throughput.
Step 2: Combine Console and File Writers
A typical service writes human-readable output to the console and persists the same stream to a file. The recommended way to compose writers is the writer_builder fluent API:
#include <kcenon/logger/builders/writer_builder.h>
auto console = writer_builder()
.console()
.build();
auto file = writer_builder()
.file("app.log")
.buffered(500)
.build();
Builder pattern for logger construction with validation.
result< std::unique_ptr< logger > > build()
logger_builder & add_writer(const std::string &name, log_writer_ptr writer)
Add a writer to the logger.
Builder pattern implementation for flexible logger configuration kcenon.
The builder takes care of decorator ordering for you and produces a single std::unique_ptr<log_writer_interface> per writer chain. The resulting chains can be passed to either logger::add_writer() or logger_builder::add_writer().
Step 3: Choose a Log Level
logger_system uses the canonical six severity levels defined by kcenon::common::interfaces::log_level:
| Level | Intended Usage |
trace | Per-message tracing for debugging hot paths |
debug | Diagnostic information useful during development |
info | High-level lifecycle and progress events |
warning | Recoverable problems that deserve attention |
error | Failed operations that the caller can react to |
critical | Severe failures requiring immediate intervention |
Set the minimum level on the logger to filter out noisy messages globally:
log->set_level(log_level::info);
log->log(log_level::trace, std::string("not emitted"));
log->log(log_level::warning, std::string("emitted"));
Step 4: Emit Structured Logs
Free-form text is fine for humans, but structured logs are far easier for log processing pipelines (Elasticsearch, Loki, Datadog, etc.) to index. Use the structured builder returned by logger::log_structured():
log->log_structured(log_level::info)
.message("User login")
.field("user_id", 12345)
.field("ip", "203.0.113.42")
.field("mfa", true)
.field("latency_ms", 42.5)
.emit();
Each call returns a builder that supports field(key, value) for any of the common scalar types (int, double, bool, std::string, std::string_view). Call emit() exactly once per record. The example structured_logging_example.cpp covers JSON and Logfmt formatters as well as thread-local context propagation.
Three Complete Examples
Example 1: Hello, Logger
#include <kcenon/common/interfaces/logger_interface.h>
namespace ci = kcenon::common::interfaces;
auto log = std::make_shared<logger>(false);
log->add_writer(std::make_unique<console_writer>());
log->log(ci::log_level::info, std::string("Hello, logger_system!"));
return 0;
}
Example 2: Console + Rotating File
#include <kcenon/logger/builders/writer_builder.h>
namespace ci = kcenon::common::interfaces;
auto console = writer_builder().console().build();
auto rotating = writer_builder()
.rotating_file("logs/app.log", 10 * 1024 * 1024, 5)
.buffered(1024)
.build();
log->log(ci::log_level::info, std::string("Service ready on port 8080"));
log->log(ci::log_level::warning, std::string("Cache miss for key user:12345"));
log->flush();
logger_builder & with_async(bool async=true)
Example 3: Structured Login Audit
#include <kcenon/common/interfaces/logger_interface.h>
namespace ci = kcenon::common::interfaces;
auto audit = std::make_shared<logger>(true);
audit->add_writer(std::make_unique<console_writer>());
audit->start();
audit->log_structured(ci::log_level::info)
.message("login_attempt")
.field("user_id", 42)
.field("result", "success")
.field("source_ip", "10.0.0.7")
.field("duration_ms", 18.3)
.emit();
audit->log_structured(ci::log_level::warning)
.message("login_attempt")
.field("user_id", 99)
.field("result", "invalid_password")
.field("source_ip", "10.0.0.9")
.emit();
audit->stop();
Next Steps