CE Logging and Observability
You are managing the logging and observability surface of the library. All changes must comply with the architectural rules in ADR-028 and the contributor guidelines in Standard-005.
Logger Domains (Mandatory)
All loggers must live under the calibrated_explanations root and use one of
the four reserved domains below:
| Domain | Usage | Example |
|---|---|---|
core |
Core runtime, caching, parallel, orchestration. | explainer.fit, ParallelExecutor |
plugins |
Builtin and external plugins, resolution. | CalibratorPlugin, PlotPlugin |
telemetry |
Metrics integration and performance shims. | LatencyMonitor, ThroughputMetric |
governance |
Audit events, checkpoints, trust decisions. | CheckpointManager, PluginTrustPolicy |
import logging
# Pattern 1: Module-level (preferred if path matches domain)
logger = logging.getLogger(__name__)
# Pattern 2: Explicit domain (if path does not match)
logger = logging.getLogger("calibrated_explanations.governance.checkpoints")
Operational vs. Governance Separation
- Governance/Audit events MUST use the
governance.*domain. These include checkpoint creation, rollbacks, binary trust decisions (accept/deny), and policy changes. - Operational events (fallbacks, cache hits, performance data) MUST use
core.*orplugins.*. - Fallbacks MUST use the
WARNINGlevel with a clear explanation of why the fallback was triggered.
Logging Context (ADR-028 §3)
Use the structured context helpers from calibrated_explanations.logging to
propagate identifiers like request_id, tenant_id, and explainer_id.
from calibrated_explanations.logging import update_logging_context, logging_context
# 1. Update global (thread-local) context
update_logging_context(explainer_id="exp-123", tenant_id="acme")
# 2. Scoped context (recommended for local blocks)
with logging_context(request_id="req-456", mode="factual"):
logger.info("Starting explanation generation")
Supported Context Keys
Must be one of: request_id, tenant_id, explainer_id, checkpoint_id,
plugin_identifier, mode.
Contributor Checklist
- No
basicConfig— never calllogging.basicConfig()in library code. - No PII — ensure no sensitive data (e.g., feature values, training data) is included in log messages.
- Structured fields — prefer passing IDs via context rather than string concatenation.
- JSON compatibility — ensure logs are compatible with structured
formatters (standard fields like
extraare supported). - Level check — use
DEBUGfor noise,INFOfor normal state,WARNINGfor fallbacks/corrections. - Exception context — use
logger.exception("message")inside except blocks to capture stack traces automatically.
Testing Logging
Verify logging output in tests using the caplog fixture:
def test_should_log_warning_when_plugin_fails(caplog):
with caplog.at_level(logging.WARNING, logger="calibrated_explanations.plugins"):
# ... trigger fallback ...
assert "fallback triggered" in caplog.text
assert any(r.explainer_id == "exp-1" for r in caplog.records)