Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
Tutorial: Basic Logging

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;
int main() {
// Create an asynchronous logger with the default queue size (8192)
auto log = std::make_shared<logger>(true);
// Attach a console writer (writes to stdout/stderr based on level)
log->add_writer(std::make_unique<console_writer>());
// Async loggers must be started before use
log->start();
log->log(log_level::info, std::string("Application started"));
log->stop(); // flushes and shuts down background workers
return 0;
}
int main()
Core console writer for logging to stdout/stderr.
Main logger implementation providing high-performance logging facilities.
Definition logger.h:157
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>
using namespace kcenon::logger;
auto console = writer_builder()
.console()
.build();
auto file = writer_builder()
.file("app.log")
.buffered(500) // batch up to 500 entries before flushing
.async(20000) // 20k-entry queue, background thread
.build();
auto log = logger_builder()
.add_writer(std::move(console))
.add_writer(std::move(file))
.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); // drop trace and debug
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>
int main() {
using namespace kcenon::logger;
namespace ci = kcenon::common::interfaces;
auto log = std::make_shared<logger>(false); // sync, no start() needed
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>
using namespace kcenon::logger;
namespace ci = kcenon::common::interfaces;
auto console = writer_builder().console().build();
auto rotating = writer_builder()
.rotating_file("logs/app.log", 10 * 1024 * 1024, 5) // 10 MB x 5 files
.buffered(1024)
.async(16384)
.build();
auto log = logger_builder()
.with_async(true)
.add_writer(std::move(console))
.add_writer(std::move(rotating))
.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>
using namespace kcenon::logger;
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