Logging¶
ETLantic provides structured, contextual logging for validation, planning, execution, and plugin coordination.
Logging is part of the shared execution experience, but ETLantic is not a log-storage platform. The core defines records, context, redaction, and lifecycle integration. Logging and observability providers route those records to the Python logging system, console, files, OpenTelemetry, cloud services, or other destinations.
Quick start (shipped)¶
JSON console logging works without extras. For OpenTelemetry, install the optional extra when available:
from etlantic.observability import JsonConsoleLogger
from etlantic.runtime.logging import RunLogger
# RunLogger redacts secrets before emission.
logger = RunLogger(run_id="run-demo", pipeline_id="demo")
logger.log("info", "starting", step_name="raw")
Use etlantic.observability for provider protocols and adapters. Prefer
public imports; do not embed secret values in log extras.
Goals¶
ETLantic logging should be:
- structured
- correlated by pipeline, plan, run, step, and attempt
- consistent across execution backends
- safe for secrets and sensitive data
- usable locally and in distributed orchestration
- compatible with standard Python logging
- separable from metrics, traces, lineage, and durable run results
Logging Versus Results and Events¶
ETLantic distinguishes three related concepts:
Lifecycle event
Immutable fact that something occurred.
Run result
Normalized outcome and measurements of completed work.
Log record
Human- and machine-readable diagnostic narrative.
For example:
event: step.started
log: Starting step "normalize_customers" with implementation "polars"
result: StepRunResult(status="succeeded", duration=1.42, ...)
Providers may derive logs from lifecycle events, but logs must not be the only source of execution state.
Structured Log Record¶
The proposed core model is:
LogRecord(
timestamp=...,
level="INFO",
code="PMEXEC100",
message='Starting step "normalize_customers"',
pipeline_id="customer_pipeline",
pipeline_version="2.1.0",
plan_id="plan_01...",
run_id="run_01...",
step_id="normalize_customers",
attempt=1,
plugin="etlantic-polars",
backend="polars",
event="step.started",
attributes={
"implementation": "polars",
"input_count": 1,
},
)
All correlation fields are optional at the model level because validation and planning logs may occur before a run exists. Providers should include every field available in the current context.
Logging Context¶
Context should propagate automatically:
Plugin authors should not manually copy identifiers into each message.
Conceptually:
with logger.bind(run=run_context):
logger.info(
"PMEXEC100",
"Starting pipeline run",
intent=request.intent,
)
with logger.bind(step=step_context):
logger.info(
"PMEXEC110",
"Starting step",
implementation=step.implementation,
)
Context propagation must work across async tasks. External orchestrator plugins must map correlation identifiers into their native task context.
Standard Logging Phases¶
Loading¶
- source or contract discovered
- format and version identified
- reference resolved
- configuration loaded
Validation¶
- validation phase started or completed
- rule or compatibility finding
- quality-gate outcome
- invalid artifact produced
Planning¶
- profile applied
- implementation selected
- capability decision
- portable protocol and definition fingerprint
- selected transformation compiler and implementation kind
- explicit native fallback reason and expression path
- execution region formed
- materialization inserted
- fallback selected
Execution¶
- run and step lifecycle
- attempt and retry behavior
- upstream artifact resolved
- resource acquired or released
- artifact materialized
- state read or committed
- callback decision
Compilation and submission¶
- backend artifact generated
- external run submitted
- remote state synchronized
- cancellation requested
Levels¶
Recommended use:
| Level | Meaning |
|---|---|
TRACE |
Fine-grained internal decisions, disabled by default |
DEBUG |
Developer-facing planning and execution detail |
INFO |
Normal lifecycle progress |
WARNING |
Recoverable degradation, fallback, or policy concern |
ERROR |
Failed operation or rejected semantic requirement |
CRITICAL |
Framework or environment failure affecting safe operation |
Diagnostics retain their own severity. A diagnostic may be rendered as a log, but diagnostic severity and log level are not interchangeable.
Stable Codes¶
Important logs should carry stable codes:
PMLOADxxx Loading and identification
PMVALxxx Validation and quality
PMPLANxxx Planning and capability resolution
PMEXECxxx Execution lifecycle
PMARTxxx Artifact handling
PMSTATExxx Incremental state and checkpoints
PMPLUGxxx Plugin behavior
PMSECxxx Security and redaction
Codes make filtering and alerting reliable even when message wording improves.
Configuration¶
[logging]
level = "INFO"
format = "console"
include_context = true
[logging.redaction]
enabled = true
keys = ["password", "token", "secret", "authorization"]
[profiles.production.logging]
level = "INFO"
provider = "opentelemetry"
json = true
Run-scoped overrides may increase verbosity:
# The CLI has no --log-level flag. Configure Python logging in your process,
# or use your orchestrator's log settings. Proposed ETLANTIC_LOG_LEVEL env
# vars are not a 0.14 CLI feature.
etlantic run customer.py:CustomerPipeline --profile development
Changing log verbosity must not change pipeline semantics or plan identity.
Python Logging Integration¶
The default provider should integrate with logging:
import logging
logging.getLogger("etlantic").setLevel(logging.INFO)
logging.getLogger("etlantic.plugin.airflow").setLevel(logging.DEBUG)
Recommended logger hierarchy:
etlantic
etlantic.loading
etlantic.validation
etlantic.planning
etlantic.execution
etlantic.plugin.<plugin-name>
ETLantic should not call logging.basicConfig() automatically in library
usage. The CLI may configure its own handlers.
Secret and Data Safety¶
Logs must never contain:
- resolved secret values
- authorization headers
- connection strings containing credentials
- unredacted environment snapshots
- complete dataframe or record contents by default
- raw invalid rows by default
- serialized plans containing secret material
Redaction should inspect both keys and registered sensitive-value types. Providers must receive already-redacted records unless explicitly operating inside a trusted secured boundary.
Data samples require a separate, explicit debug policy:
Plugin Requirements¶
Plugins must:
- use the logging context supplied by ETLantic
- preserve correlation identifiers
- avoid adding their own global handlers
- classify expected failures with stable error categories
- redact plugin configuration and backend messages
- avoid logging the same exception repeatedly at multiple layers
Plugins may attach backend metadata such as job, query, task, or cluster IDs.
External Orchestrators¶
External runtimes often own their own logs. The orchestration plugin should map:
ETLantic run_id ↔ orchestrator run identifier
ETLantic step_id ↔ task identifier
ETLantic attempt ↔ task attempt
ETLantic trace_id ↔ native trace context
ETLantic should link to native logs rather than copying an unlimited remote log stream into the core result.
Local Debugging¶
Interactive execution should make upstream-result resolution visible:
DEBUG PMART110 Reusing output "normalized.result" from current run context
DEBUG PMPLAN214 Keeping edge in Polars lazy execution region
INFO PMEXEC110 Starting step "score_customers"
This is especially important when a result is reused, invalidated, cached, or materialized.
Testing¶
Tests should assert structured fields rather than formatted strings:
def test_step_log_contains_context(captured_logs):
record = captured_logs.find(code="PMEXEC110")
assert record.run_id == "run-123"
assert record.step_id == "normalize_customers"
Conformance tests should verify:
- correlation propagation
- redaction
- deterministic codes
- exception normalization
- async isolation
- no secret leakage
Key Principle¶
ETLantic defines one safe, structured logging context across every backend. Providers control routing and storage; logs never replace lifecycle events or normalized results.