cadrumo.core.logging module¶
Logging configuration entry point.
Provides get_logger() as the consistent logger factory to avoid scattered
bare logging instances, with configure_logging() installing the project
defaults. The installed log-record factory reads
cadrumo.core.observability.sink.JsonlRunSink state indirectly through
contextvars, so every record automatically picks up the active run_id /
step_id while a run context is bound.
This module attaches the log-record secret scrubber. Every handler
attached through configure_logging() receives a
SecretScrubbingFilter so sensitive fields are redacted before
formatting. Shape-based NIF, URL, and bearer-token matching is delegated
to redact_for_log(); this module keeps only
logging-specific key-paired placeholders such as cookies, passphrases, and
certificate serial suffixes. Per-run JSONL handlers are attached with
attach_run_sink() so the same filter protects observability output.
Encoded document payloads are contained at two seams, because the diagnostic
log is plaintext on disk and a scanned invoice that reaches it has left secure
storage. _ThirdPartyDebugFilter holds non-Cadrumo loggers below DEBUG
at every sink, so a model or HTTP client never gets to dump a request body
carrying an image; SecretScrubbingFilter then redacts base64 payloads
out of the records that remain, including a payload reachable only by rendering
an opaque %s argument. The first seam needs no knowledge of the payload’s
shape, and the second covers first-party records the first seam admits.
Logging is a diagnostic channel, not the CLI result contract. Operator-facing
success payloads and typed Notice values are
rendered through the JSON/text output stack; this module only prepares redacted
log records and plaintext diagnostic log files rooted by settings.
- SCRUB_FIELD_PATTERNS: tuple[str, ...]¶
ALWAYS_REDACT_KEY_TERMS(the tree-wide shared base) plus this module’s own generic logging-domain additions. Seecadrumo.core.redaction.ALWAYS_REDACT_KEY_TERMSfor why the base is never redeclared independently.- Type:
Log-message KEY-NAME redaction vocabulary
- class LogExtra(root=PydanticUndefined, **data)[source]¶
Bases:
RootModel[dict[str, Union[str, int, float, bool, NoneType]]]Typed logging
extrapayload restricted to flat loggable scalars.Upgrades the historical
Mapping[str, object]return annotation on service-layeras_extra()log-field helpers (e.g.PerModeloAggregationLogFields,OperatorSurfaceContract’slog_fields): every current emitter only ever writes flat, non-secret scalars intologging.Logger.debug()’sextra=mapping, so accepting bareobjectvalues understated the real contract. A future field that accidentally carried a nested mapping, a Decimal, or another non-serialisable value now fails loudly at construction with apydantic.ValidationErrornaming the offending field, instead of surfacing later as a JSONL-sink serialisation surprise.Use
for_logging()to materialise the plaindictthe stdlibloggingmodule’sextra=parameter expects; the model itself is not iterable the waylogging.Logger.makeRecord()requires.- Parameters:
root (RootModelRootType)
- class SecretScrubbingFilter(name='')[source]¶
Bases:
FilterRedact sensitive fields from log records before formatting.
The filter mutates each
logging.LogRecordin place so handlers, stderr diagnostics, and JSONL run sinks see the same scrubbed record. It is deliberately narrower than CLI output redaction: structured command results still route throughcadrumo.core.output_renderingorcadrumo.core.json_contract, while this filter protects logging-only message text, %-format args, exception text, andextrafields.
- class DropRunEventFilter(name='')[source]¶
Bases:
FilterSuppress observability
run_eventrecords on the stderr handler.Records carrying a
run_eventextra are the per-run JSONL sink’s diet — they’re already persisted toevents.jsonlviaJsonlRunSink. Echoing them on stderr as well would spam the console with onerun.event NAVIGATIONline per step; suppressing them here removes the noise while leaving the record intact for any other handler (including the JSONL sink).
- OPERATOR_DOCUMENT_LOG_EXTRA¶
Extra key marking a record whose content the emitter has ALREADY written to the operator’s stream as a structured document. Pass it through
extraon any log call that accompanies such a write.
- class DropOperatorDocumentEchoFilter(name='')[source]¶
Bases:
FilterSuppress records already rendered as an operator document, on stderr only.
stderr is the machine-readable error channel: the CLI’s terminal boundary writes the JSON error document there, and an agent or script parses that stream. A diagnostic log record about the same failure is therefore a duplicate on stderr, and a harmful one — the record carries the raw exception and its traceback, so echoing it prepends internal frames and the unredacted exception text to a document whose whole purpose is to present a translated, redacted message instead.
Suppressing the echo here keeps the record intact for every other handler, so the rotating file sink still receives the full traceback. That matters: the INTERNAL document tells the operator to consult the diagnostic logs, so the traceback has to actually be in them.
- default_log_file_path()[source]¶
Return the file path for non-interactive project logs.
Resolved through the taxonomy accessor rather than by reading
cadrumo_log_dirhere. Both answer the same today, because the settings field is what the accessor consults first – but only one of them stays correct if the member’s resolution ever gains a case. A call site that reads the field directly has quietly opted out of whatever the accessor decides, and it does so invisibly, by continuing to work.An explicit
CADRUMO_LOG_DIRoverride still wins, and still keeps the log isolated per workspace rather than mixing every session’s records into one system-wide file.The filename itself is read off the taxonomy’s
LOG_FILEmember rather than declared as an untethered string literal here.- Return type:
Path
- configure_logging()[source]¶
Configure the project-wide diagnostic logging defaults.
Installs settings-derived stderr/file handlers, the run-context record factory, and
SecretScrubbingFilteron the root logger plus every configured handler. The file handler writes redacted diagnostic plaintext underdefault_log_file_path(); this module does not encrypt logs or persist them through secure-object repositories.When the diagnostic log directory cannot be created (an inaccessible
CADRUMO_LOCAL_STORAGE_ROOT/CADRUMO_LOG_DIR), logging degrades to stderr-only and records an instructive diagnostic naming the likely remedy — it never crashes CLI startup with a raw traceback.The function is idempotent, so hosts and the first-record placeholder can both reach it without duplicating handlers.
- Return type:
- set_log_level(level, *, file_level=10)[source]¶
Apply
levelto the root logger and every attached handler.The root logger itself is always set to
logging.DEBUGso no record is discarded before reaching a handler; each handler then applies its own level gate.FileHandlerinstances receivefile_level(defaultDEBUG) to keep the diagnostic log comprehensive. All other handlers (typically the stderr stream handler) receivelevel.configure_logging()is called first so the dictConfig contract is in place before any level mutation.- Parameters:
level (
int) – The effective level for non-file handlers (e.g.logging.INFOfor verbose mode).file_level (
int) – The level applied tologging.FileHandlerinstances (defaultlogging.DEBUG).
- Return type:
- attach_run_sink(sink)[source]¶
Install
SecretScrubbingFilteronsinkthen attach it to root.Ensures every record flowing through the JSONL run sink is scrubbed before it reaches the serialiser, even when the root-logger filter has already scrubbed the shared record in-place. The filter is idempotent: a second call with the same sink is a no-op because the guard checks
root_logger.handlersfor an existing instance.- Parameters:
sink (
Handler) – Thelogging.Handler(typicallycadrumo.core.observability.sink.JsonlRunSink) to attach to the root logger.- Return type:
The sink is a diagnostic observability target. It receives redacted log records, not CLI result payloads or secure-storage records.
- detach_run_sink(sink)[source]¶
Remove
sinkfrom the root logger and perform symmetric teardown.Reverses every side-effect of
attach_run_sink(): the handler is removed from the root logger, theSecretScrubbingFilterinstances thatattach_run_sink()installed on the sink are removed, and the sink is flushed so in-flight records reach their destination before the handle is released.The caller is responsible for closing the sink after detach; this function deliberately does not call
close()so a caller can flush output and inspect state before teardown.- Parameters:
sink (
Handler) – Thelogging.Handlerpreviously attached byattach_run_sink().- Return type:
- defer_logging_configuration()[source]¶
Keep CLI parse/preflight state-free until dispatch is authorized.
- Return type:
- resume_logging_configuration()[source]¶
Release a prior startup deferral without configuring by itself.
- Return type:
- get_logger(name)[source]¶
Return the project logger for the given module name.
Preferred over direct
logging.getLogger()in production modules because it attachesSecretScrubbingFilterdirectly to the returned logger. It never configures logging: hosts callconfigure_logging()at their process boundary, and a record that reaches the root first installs the configuration itself.