ExAgent. Observability. OpenTelemetry
(ExAgent v2.0.1)
View Source
Optional native OpenTelemetry instrumentation, using the application's tracer.
tracing = ExAgent.Observability.OpenTelemetry.new()
agent = ExAgent.new(model: model, observability: tracing)Add :opentelemetry_api, an SDK and an exporter in the host application and
configure ExAgent.Observability.BoundedProcessor for finite admission and
observable drops/export failures (see docs/guides/observability.md). The native SDK's
batch processor has a soft queue threshold rather than that hard bound. This
module never starts or replaces a tracer provider, configures an exporter, or
performs network IO. :tracer may be an application-supplied native tracer;
otherwise the instrumentation scope is :exagent, version "1".
The small exagent.gen_ai.v1 profile uses gen_ai.usage.* on model requests
only. Run totals use exagent.usage.* and are inclusive of descendants; do not
sum them with generation usage. Costs are estimates in cents from the existing
execution ledger, not a second pricing callback or an authoritative bill.
The profile is pinned to GenAI commit
b5d8440f6f126738fd50f927752cd669772c517b; no schema URL is invented. Native
Anthropic input is cache-exclusive and is normalized only for generation
gen_ai.usage.input_tokens; OpenAI-compatible built-ins already report an
inclusive total. Custom model semantics are unknown: their input remains in
exagent.usage.reported_input_tokens instead of claiming a GenAI total. Run
aggregates retain the existing ledger's provider-native token semantics.
A durable pause ends its run span with exagent.status=paused, without an OTel
error. Resume creates a new attempt/span under the same run identity. Run usage
and host request/tool counts are cumulative lifetime snapshots, not attempt
deltas or successful-effect counts; do not sum them across resumed attempts.
exagent.continuation.* contains only the validated public reference identity
and numeric version/revision, never approval actors, tokens or stored records.
A Server terminal synthesized after abort/worker loss has only its last
progress snapshot: exagent.usage.source=last_progress, partial usage and an
unknown total cost. Observed token/count subtotals are retained, and any known
monetary subtotal uses exagent.cost.known_subtotal_cents instead of claiming
a complete exagent.cost.cents. Normal core results keep their reported status.
Content is off by default. Opting in requires both content: true and
redact: fn field, value -> {:ok, safe_binary} | :drop end. The callback sees
only explicitly selected prompt/output/messages/tool arguments/results, never
model structs, dependencies, configuration, exceptions or hidden reasoning.
Content is admitted only after successful redaction, valid UTF-8 and byte-limit
checks. Invalid/raising callbacks and oversized values drop the entire field.
Opaque values and structs are dropped; redaction input is bounded plain data.
No prefixes of unredacted content are exported. Redactors are trusted local
callbacks and should be fast; they are not sandboxed.
max_content_bytes defaults to 4096 per field (at most two fields per span).
max_content_input_bytes defaults to 65536; oversized inputs are not sent to
the redactor. Labels/IDs are bounded to 256 bytes. Use non-secret identifiers
for agent, tool, model and correlation names. Arbitrary metadata, baggage,
provider bodies, exception messages and stacktraces are never attributes.
capture_context/0 and with_context/2 propagate only span context and this
configuration across application-owned tasks. These handles are ephemeral,
not snapshot/JSON data. Server queues capture context at admission; a public
lazy stream captures it at enumeration, unless trace_context: is supplied.
Every attachment restores both prior OTel context and Logger metadata.
Native SDK span storage provides idempotent completion. A monitor per recording
operation also closes its span when the executing process dies (including
:kill), without another tracing registry or export queue. Hard termination
cannot recover unreported usage and is marked partial. SDK shutdown or VM death
can lose spans. Arbitrary synchronous processors/samplers installed by the host
can block instrumentation; use the bounded batch setup in the example.
Instrumentation failures emit [:exagent, :observability, :failure] with a
numeric count and a fixed stage. Redaction drops emit
[:exagent, :observability, :content_dropped], without content or raw errors.
Summary
Functions
Capture a runtime-only span context; baggage and arbitrary context values are excluded.
Build an opt-in instrumentation configuration without changing the SDK.
Run a function under a captured context, restoring the caller even on exceptions.