Skip to content

OpenTelemetry Tracing

@reactive-agents/observe bridges the agent event bus to OpenInference-compliant OpenTelemetry spans. Every agent run automatically emits a span hierarchy — workflow → LLM calls → tool calls — that any OTLP-compatible backend can ingest.

Terminal window
npm install @reactive-agents/observe
# or
bun add @reactive-agents/observe

Set OTEL_EXPORTER_OTLP_ENDPOINT and call autoConfigureExporter before running agents:

import { autoConfigureExporter } from "@reactive-agents/observe"
import { OpenInferenceTracerLayer } from "@reactive-agents/observe"
// OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 already set in env
const handle = autoConfigureExporter({ serviceName: "my-agent" })
// Wire tracer layer into your Effect runtime...
// (see "Effect integration" section below)
await handle.shutdown() // flush before process exit

autoConfigureExporter is a no-op when OTEL_EXPORTER_OTLP_ENDPOINT is not set — safe to ship in all environments.

Each agent run produces a nested span tree:

agent:my-agent-id ← openinference.span.kind = AGENT
llm:anthropic/claude-... ← openinference.span.kind = LLM
tool:web-search ← openinference.span.kind = TOOL
llm:anthropic/claude-...

All child spans share the workflow trace ID, so backends show the full call graph per invocation.

AttributeDescription
openinference.span.kindAGENT
llm.model_nameModel at start
llm.providerProvider name
agent.idAgent identifier
task.idTask correlation ID
agent.iterationsTotal reasoning loop iterations
llm.token_count.totalAggregate tokens across run
agent.successBoolean — false on error
AttributeDescription
openinference.span.kindLLM
llm.model_nameModel name
llm.providerProvider
llm.token_count.promptInput tokens
llm.token_count.completionOutput tokens
llm.token_count.totalTotal tokens
llm.estimated_cost_usdEstimated cost
llm.cachedtrue when served from prompt cache
llm.duration_msRound-trip latency
AttributeDescription
openinference.span.kindTOOL
tool.nameTool identifier
tool.parametersJSON-serialized arguments
tool.outputJSON-serialized result
agent.iterationReasoning loop iteration
tool.duration_msExecution latency
tool.successBoolean — false on error

OpenInferenceTracerLayer is an Effect Layer that subscribes to the EventBus. Provide it alongside your other layers:

import { Effect, Layer } from "effect"
import { EventBusLive } from "@reactive-agents/core"
import { OpenInferenceTracerLayer } from "@reactive-agents/observe"
import { autoConfigureExporter } from "@reactive-agents/observe"
const handle = autoConfigureExporter({ serviceName: "my-agent" })
const AppLayer = Layer.merge(
EventBusLive,
OpenInferenceTracerLayer,
// ... other layers
)
await Effect.runPromise(
myAgentProgram.pipe(Effect.provide(AppLayer))
)
await handle.shutdown()
import { setupOpenInferenceExporter } from "@reactive-agents/observe"
const handle = setupOpenInferenceExporter({
endpoint: "http://my-collector:4318",
serviceName: "production-agent",
headers: {
Authorization: `Bearer ${process.env.BACKEND_TOKEN}`,
},
})

@reactive-agents/observe emits standard OTLP HTTP spans with OpenInference semantic attributes. Works out of the box with:

  • JaegerOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
  • Grafana TempoOTEL_EXPORTER_OTLP_ENDPOINT=https://tempo.example.com
  • Langfuse — set endpoint + Authorization header
  • Arize PhoenixOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:6006
  • Any OTLP HTTP-compatible collector

@reactive-agents/observe is @stable as of v0.11. The OpenInferenceTracerLayer, setupOpenInferenceExporter, and autoConfigureExporter exports are stable. Additional exporters (Langfuse, Braintrust) and sampling support are planned for v0.11.1.