Overview
Add property-based coverage for the structured logging hook in
cuprum/adapters/logging_adapter.py, closing the last gap left by issue #78.
Background
Issue #78 is titled "Hypothesis stateful tests for metrics/tracing/logging
hooks", but its body scopes the work to cuprum/adapters/metrics_adapter.py.
Taking the title at its word, the three adapters now stand as follows:
| Adapter |
Randomised event-sequence coverage |
tracing_adapter.py |
cuprum/unittests/test_tracing_span_stateful.py (pre-existing) |
metrics_adapter.py |
cuprum/unittests/test_metrics_adapter_stateful.py (added in #247) |
logging_adapter.py |
none |
So only the logging hook is outstanding.
Why a state machine is the wrong shape here
RuleBasedStateMachine earns its keep in the other two adapters because both
hold an active map keyed by ExecId — TracingHook._active_spans and the
metrics collector's in-flight state — and the interesting bugs are drain and
correlation bugs across interleaved sequences.
structured_logging_hook has no such state. It is a pure per-event function:
it maps a phase to a level, builds an extra mapping, formats a message, and
emits exactly one record. There is no map to drain and no cross-event
correlation to break, so a state machine would generate interleavings that
cannot distinguish any two implementations.
@given over generated ExecEvent values (and short sequences) is the shape
that actually tests the behaviour.
Proposed properties
- One record per event. Every event emits exactly one
LogRecord; no
phase is silently dropped.
- Level mapping is total. The
level_map in logging_adapter.py:108
covers every phase the event model can produce, and an unmapped phase falls
back to the documented default rather than raising.
- Field namespacing. Every attribute
_build_extra attaches is
cuprum_-prefixed, so records cannot collide with LogRecord's reserved
attribute names — this is the failure mode that would surface as a
KeyError at emit time deep in a user's logging configuration.
- JSON round-trip. For any event,
JsonLoggingFormatter.format produces
a string that json.loads accepts, and _json_serializable leaves no value
that json.dumps would reject.
- Message formatting is total.
_format_message produces a non-empty
string for every phase, including events with absent optional fields.
Benefit
The JSON round-trip and field-namespacing properties are the valuable ones:
both fail only for specific event shapes that hand-written tests are unlikely
to enumerate, and both fail inside the user's logging stack rather than in
cuprum, which makes them expensive to diagnose in the field.
Cost trade-off
Low. The event strategies already exist in
cuprum/unittests/test_metrics_adapter_stateful.py and can be shared.
Recommended order
After the current property-test tranche lands. No production refactor is
needed first — unlike the metrics work, the logging reducers are already pure.
Raised from PR #247, which closes the
metrics portion of #78.
Overview
Add property-based coverage for the structured logging hook in
cuprum/adapters/logging_adapter.py, closing the last gap left by issue #78.Background
Issue #78 is titled "Hypothesis stateful tests for metrics/tracing/logging
hooks", but its body scopes the work to
cuprum/adapters/metrics_adapter.py.Taking the title at its word, the three adapters now stand as follows:
tracing_adapter.pycuprum/unittests/test_tracing_span_stateful.py(pre-existing)metrics_adapter.pycuprum/unittests/test_metrics_adapter_stateful.py(added in #247)logging_adapter.pySo only the logging hook is outstanding.
Why a state machine is the wrong shape here
RuleBasedStateMachineearns its keep in the other two adapters because bothhold an active map keyed by
ExecId—TracingHook._active_spansand themetrics collector's in-flight state — and the interesting bugs are drain and
correlation bugs across interleaved sequences.
structured_logging_hookhas no such state. It is a pure per-event function:it maps a phase to a level, builds an
extramapping, formats a message, andemits exactly one record. There is no map to drain and no cross-event
correlation to break, so a state machine would generate interleavings that
cannot distinguish any two implementations.
@givenover generatedExecEventvalues (and short sequences) is the shapethat actually tests the behaviour.
Proposed properties
LogRecord; nophase is silently dropped.
level_mapinlogging_adapter.py:108covers every phase the event model can produce, and an unmapped phase falls
back to the documented default rather than raising.
_build_extraattaches iscuprum_-prefixed, so records cannot collide withLogRecord's reservedattribute names — this is the failure mode that would surface as a
KeyErrorat emit time deep in a user's logging configuration.JsonLoggingFormatter.formatproducesa string that
json.loadsaccepts, and_json_serializableleaves no valuethat
json.dumpswould reject._format_messageproduces a non-emptystring for every phase, including events with absent optional fields.
Benefit
The JSON round-trip and field-namespacing properties are the valuable ones:
both fail only for specific event shapes that hand-written tests are unlikely
to enumerate, and both fail inside the user's logging stack rather than in
cuprum, which makes them expensive to diagnose in the field.
Cost trade-off
Low. The event strategies already exist in
cuprum/unittests/test_metrics_adapter_stateful.pyand can be shared.Recommended order
After the current property-test tranche lands. No production refactor is
needed first — unlike the metrics work, the logging reducers are already pure.
Raised from PR #247, which closes the
metrics portion of #78.