Skip to content

Observability

Every run emits events (run.started, step.succeeded, item.rejected, budget.exceeded, ...). agentl ships listeners that turn them into logs, traces, metrics and error reports.

Want CLI Python Install
JSON logs --log-format json or AGENTL_LOG_FORMAT=json JsonLogs() built in
Traces and metrics --otel OpenTelemetry() agentl-etl[otel] + your exporter
Error tracking --sentry (reads SENTRY_DSN) Sentry(dsn) agentl-etl[sentry]
import agentl
from agentl.observability import JsonLogs, OpenTelemetry, Sentry

agentl.run("pipeline.yaml", listeners=[JsonLogs(), OpenTelemetry(), Sentry()])

A listener is any callable that takes an event dict, so writing your own is one function.

JSON logs

One object per line on stderr (or a logging.Logger with JsonLogs(logger=...)):

{"level": "info", "run_id": "20261013-214022-d6135f", "pipeline": "sales", "ts": 1760391622.1,
 "type": "step.succeeded", "step": "clean", "data": {"records": 120, "seconds": 0.4}}

level is error for *.failed and budget.exceeded, warning for item.rejected, info otherwise. Ship it with whatever already collects your logs (Loki, CloudWatch, Datadog, ELK).

OpenTelemetry

One trace per run, with a child span per step. Spans carry records, duration, tokens and cost, and are marked as errors when a step or the run fails; rejected records and budget.exceeded become span events.

Metrics:

Name Type Labels
agentl.runs counter pipeline, status
agentl.records counter pipeline, step
agentl.rejected counter pipeline, step
agentl.tokens counter pipeline, step, direction
agentl.cost_usd counter pipeline, step
agentl.step.duration histogram (s) pipeline, step

agentl uses the global OpenTelemetry providers, so configure exporters the standard way, for example:

pip install 'agentl-etl[otel]' opentelemetry-distro opentelemetry-exporter-otlp
export OTEL_SERVICE_NAME=etl OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
opentelemetry-instrument agentl run pipeline.yaml --otel

Sentry

Failed runs become Sentry issues tagged with agentl.pipeline, agentl.run_id and the failing agentl.step, grouped per pipeline and step, with the run summary as context. Sentry() reuses an SDK you already initialized; Sentry(dsn=...) initializes it.