Known limitations¶
Most of these are deliberate design choices.
The rings are lossy¶
- The shared ring has no backpressure. When producers outrun the writer thread, older entries
are overwritten, and string data can be overwritten before the entry that references it is
read. Thread rings preserve unread entries: a full ring spills into the shared ring by default,
or discards the new entry with
RingFullPolicy::Drop. See Architecture and Per-thread rings. LogStats::entry_loss_countcounts shared-ring overwrites only whenSLICK_LOGGER_ENABLE_LOSS_DETECTION=ON. This includes entries the writer discards after a producer rewrites them during copying. Detection costs the writer a compare per read and an atomic add per lossy read;kLossDetectionEnabledreports whether it is enabled. Entries dropped underRingFullPolicy::Dropare counted with either setting, soentry_loss_countcan be non-zero with detection off. See Queue tuning and What is measured.string_loss_countis always0by construction: slick-queue counts losses insideread(), and the logger never calls it on the string ring.
Format strings are stored by pointer¶
- A
LOG_*call recognizes a string literal by its type (const char(&)[N]) and queues only the pointer. Any otherconst char[N]lvalue, such as a class member or a localconst chararray, has the same type and is treated the same way, so changing or destroying it before the writer thread consumes the entry corrupts the output. Passstd::string_view{member}instead. See Formatting. - A pre-built
std::format_argscannot carry types that need their ownstd::formatterspecialization: those are logged as<handle>. Use the variadic call for them. See Passing std::format_args.
Fixed limits¶
- A log call takes at most
SLICK_LOGGER_MAX_ARGSformat arguments (20 by default). Redefine it before includingslick/logger.hpp. See Log macros and levels. - A single binary payload is capped at
kMaxPayloadBytes(65534) and truncated beyond it, because slick-queue packs its reservation size into 16 bits. Split a larger buffer across arguments of one call: the cap per entry isSLICK_LOGGER_MAX_ARGS * kMaxPayloadBytes(~1.28 MB by default). See Payloads larger than 64KB. process_tagis truncated to 15 bytes, on a UTF-8 character boundary. See Multi-process logging.
Build settings that must match¶
- A plugin and its host share one
Loggerobject, and processes logging into one shared-memory segment share its layout. Either way, all of them must be built from the same slick-logger version with the sameSLICK_LOGGER_MAX_ARGS, and processes on one segment also need the same architecture. A layout mismatch throws at attach time in the multi-process case, but is undefined behavior across a shared library boundary, where more settings must match. See Shared libraries and Constraints. - Turning off source locations, at build time (
SLICK_LOGGER_ENABLE_SOURCE_LOCATION=OFF) or at runtime (set_source_location_enabled(false)), leaves entries with nofile:line. - Compiling out thread-id capture (
SLICK_LOGGER_ENABLE_THREAD_ID=OFF) makesset_pattern("%t")throw and name the option, rather than rendering a flat0. See Thread id capture.
Multi-process specifics¶
- Pointer arguments print addresses that only mean something inside the producing process.
- Each process stamps its own entries with its timestamper, so every process needs a clock on the same epoch.
- Sink indices used by
log_to_sink()andISink::log()resolve against the collector's sink list. - On POSIX, segment cleanup is manual by design: slick-logger never
shm_unlinks the queue, string or registry segments. Windows removes them once the last handle closes. The commands are in Constraints. - A producer killed between reserve and publish leaves a hole in the shared ring, which the
collector abandons after
stalled_entry_timeout_msand logs as a warning.
Not implemented¶
RotationConfig::compress_oldis a placeholder; rotated files are not compressed. See Rotation configuration.