Skip to content

Observability

Structured logs + OpenTelemetry span-name constants. Importing tape.obs has zero side effects and zero third-party dependencies — adapters install hooks.

from tape.obs import log_json, span, SPAN_DISPATCH_EFFECT

log_json("effect.dispatched", run_id="r-1", tool="wire_money", reactor="outbox")

with span(SPAN_DISPATCH_EFFECT, run_id="r-1") as sp:
    ...  # your code; sp is None if no OTel tracer is installed

Observability — structured-log helpers and OpenTelemetry span-name constants.

Span names (use as the operation name when starting a span — emitter is free to use OTel SDK, the agnostic tape.obs.span(name, **attrs) helper here, or nothing at all):

tape.begin_run, tape.resume_run,
tape.record_decision,
tape.begin_effect, tape.complete_effect,
tape.reconcile_effect, tape.dispatch_effect,
tape.compensate, tape.redrive,
tape.await_signal, tape.send_signal

Structured log fields — emit these as JSON when available::

tenant_id, app_name, run_id, invocation_id, session_id, seq,
effect_key, decision_index, reactor, lease_owner

The log_json(...) helper writes one JSON line to stderr with stable key ordering; the span(...) helper is a no-op context manager when opentelemetry is not installed.

log_json

log_json(msg: str, *, level: str = 'INFO', **fields: Any) -> None

Emit one structured JSON line to stderr, following STRUCTURED_FIELDS.

span

span(name: str, **attrs: Any) -> Iterator[Any]

Open a span if OpenTelemetry is installed, otherwise yield None.

Importing OTel lazily keeps tape usable with no extra deps.

configure_cloud_trace_exporter

configure_cloud_trace_exporter(project_id: Optional[str] = None) -> bool

available. Returns True on success, False otherwise (so callers can warn once at startup).