|
Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
|
This page collects the most common questions external developers ask when adopting logger_system. For step-by-step walkthroughs see the Tutorial: Basic Logging, Tutorial: Decorator Composition, and Tutorial: Production Configuration tutorials.
Use rotating_file_writer (or writer_builder::rotating_file) and supply the maximum segment size and the number of segments to retain. logger_system performs size-based rotation only; calendar rotation is best handled by an external tool such as logrotate running on the host.
Place log files on a dedicated volume to avoid filling the root partition, and compress old segments out-of-band to keep the hot path predictable.
| Aspect | Synchronous | Asynchronous |
|---|---|---|
| Latency on caller | Bound to writer cost | Sub-microsecond enqueue |
| Throughput | Limited by writer | 4M+ msg/s with batching |
| Determinism | Strict ordering on the calling thread | Reordered relative to enqueue |
| Failure surface | Caller sees the error | Worker may drop or block |
| Best for | Tests, CLIs, audit pipelines | Services, batch jobs |
Choose sync when each log() call must be durable on return (auditing, crash diagnostics). Choose async (the default) for everything else. You can combine both: build an async chain for general logging and a sync critical chain for audit-grade events.
Implement kcenon::logger::log_writer_interface. The required surface is small: a write(const log_entry&) returning common::VoidResult, plus flush() and get_name(). Once implemented, your writer plugs into any decorator chain or directly into logger::add_writer().
See custom_writer_example.cpp for a complete reference implementation.
logger_system is Tier 2 in the kcenon ecosystem; monitoring_system (Tier 3) consumes it via the monitoring_backend. Two integration paths are supported:
LOGGER_USE_MONITORING=ON and the monitoring_backend will publish queue depth, drop counts, and writer latency to monitoring_system's metric registry.kcenon::common::interfaces::IObserver and forward logger::metrics() snapshots into your existing dashboard pipeline (Prometheus, OTLP, etc.).The two paths are not mutually exclusive: keep the in-process metrics for local debugging and ship aggregated counters via OTLP for fleet-wide dashboards.
Yes. The logger class is fully thread-safe, and all built-in writers are either inherently thread-safe (console_writer, file_writer, rotating_file_writer, network_writer, otlp_writer) or wrapped in a mutex via thread_safe_writer when needed. The async pipeline uses a lock-free MPSC queue so contention does not scale with thread count.
If you implement a custom writer, you can either:
writer_builder::thread_safe() to inherit a mutex-guarded facade.In rough order of impact:
buffered (4k-16k entries) and batch (1k-4k entries) to amortize syscalls.set_level) instead of relying on a downstream filter.log_structured() over format()-style messages on the hot path; it avoids constructing and copying intermediate strings.LOGGER_USE_THREAD_SYSTEM=ON if you already have a tuned thread pool to share.Build with LOGGER_ENABLE_OTLP=ON and link opentelemetry-cpp (>= 1.14). Construct an otlp_endpoint, point it at your collector, and add the otlp_writer to your logger:
For high availability, run a sidecar collector (Vector, OTel Collector, Fluent Bit) on each host so transient network issues do not back up the in-process queue.
Yes, ordering changes both correctness and performance. From outermost (the caller-facing wrapper) to innermost (the core writer), the recommended order is:
async_writerthread_safe_writer (only when wrapping a non-thread-safe core)buffered_writer / batch_writerencrypted_writerfiltered_writerformatted_writerfile_writer, console_writer, etc.)Putting async_writer inside buffered_writer defeats async (the buffer flushes on the calling thread). Putting encrypted_writer outside the buffer wastes CPU because each entry is encrypted individually. See Tutorial: Decorator Composition for a worked example.
Adopt a stable, lowercase, snake_case schema and reuse the same key names across services. The kcenon convention follows ECS-inspired families:
| Family | Examples |
|---|---|
| Identity | user_id, tenant_id, session_id |
| Network | source_ip, dest_ip, port, protocol |
| Tracing | trace_id, span_id, parent_span_id |
| Performance | duration_ms, latency_us, bytes_in, bytes_out |
| Result | status_code, outcome, error_code, error_message |
| Resource | service_name, service_version, host, region |
Stick to scalars (int, double, bool, string) on the hot path; nested objects force the formatter to do extra work. If you need hierarchical data, flatten it into dotted keys (request.size_bytes).
Build with LOGGER_USE_ENCRYPTION=ON (the default) to enable the encrypted_writer and secure_key_storage. Generate a 32-byte AES key and chain encrypted after buffered so each cipher block carries enough data to be efficient:
Additional hardening:
chmod 600).examples/ for runnable, idiomatic snippets.