Performance Guide¶
obslog is designed to be cheap when idle and predictable under load (PRODUCT.md §15.1,
NFR-PERF-001..006). Optimization decisions are measurement-driven (PRIN-001); this
guide explains the model and how to measure.
Pay for what you use¶
A log call whose level is below the active threshold short-circuits before building a
record or evaluating any lazy field (NFR-PERF-001). In the committed benchmarks a
disabled emit is roughly an order of magnitude cheaper than an enabled one — the gate
is a single integer comparison.
# `expensive()` runs only if DEBUG is enabled:
log.debug("cache.snapshot", state=obslog.lazy(expensive))
Wrap any costly field value in obslog.lazy(...) (API-007) so it is computed only
when the record is actually emitted.
Cheap context¶
bind() returns a new logger holding only the merged context delta (NFR-PERF-002);
it does not touch sinks or build records. Ambient context() uses contextvars, which
is O(1) to read on the hot path.
Allocation-light emit path¶
On an enabled emit the record is built once and passed to sinks by reference
(NFR-PERF-003). The resource mapping is shared (read-only) rather than copied per
record. Source/caller capture (stack inspection) is opt-in because of its cost
(NFR-PERF-004).
High-volume delivery: Batching¶
For sinks backed by expensive I/O (network exporters, disk), wrap them in
obslog.sinks.Batching to amortize flushes (NFR-PERF-006). It buffers records and
flushes by size and/or on a background interval, and prefers a downstream
emit_batch(records) method when present. Batching is an optional sink concern — it
does not change the core Sink contract (FR-005).
from obslog.sinks import Batching, Stream
with Batching(Stream(), max_batch=500, flush_interval=1.0) as sink:
obslog.configure(sinks=[sink])
... # flushed on size, interval, close(), and at process exit
Measuring¶
The benchmark suite lives in tests/benchmarks/ and is deselected from the normal test
run. Run it explicitly:
pytest tests/benchmarks -m benchmark
CI runs it on every push and uploads the results, so regressions in the enabled-emit,
disabled-emit, bind, and context paths are visible over time (NFR-PERF-005).