← All extensions
OTel Export banner

OTel Export

ivyx

Ships run traces to an OpenTelemetry collector so your existing tracing tools can read them

OTel Export

Every governed call and every agent turn already produces an OpenTelemetry-shaped span. This sends them to a collector you name, so the tools your team already uses for tracing can read a run from outside the app.

What you can do

  • Send traces to a collector you already run. An OpenTelemetry Collector, Grafana Tempo, Jaeger, Langfuse, Phoenix, Datadog: anything that speaks OTLP over HTTP accepts these traces with no adapter in between.
  • Read an agent run as a tree. A turn is a span, the model call and every tool the model asked for hang beneath it, and a graph containing a turn is one trace rather than two.
  • See a refusal as a failure. A call the policy stopped arrives as an error span carrying the rule's own reason, so a blocked run reads as blocked rather than as a run that gave up.
  • Follow token counts. Where the model reported them, a turn carries gen_ai.usage.input_tokens and gen_ai.usage.output_tokens under the standard names.
  • Check what is leaving. OTel Export: Show Export Status reports what was sent, what is waiting, what was dropped and the last error, in the collector's own words.

Getting started

  1. Run a collector, or use one you already have. The quickest local one:

    # config.yaml
    receivers:
      otlp:
        protocols:
          http:
            endpoint: 0.0.0.0:4318
    exporters:
      debug:
        verbosity: detailed
    service:
      pipelines:
        traces:
          receivers: [otlp]
          exporters: [debug]
    
    docker run --rm -p 4318:4318 \
      -v "$PWD/config.yaml:/etc/otelcol-contrib/config.yaml" \
      otel/opentelemetry-collector-contrib:latest
    
  2. Confirm it is up by opening http://localhost:4318 — a collector answers a plain-text or JSON status there, and the receiver in this repo reports how many spans it has seen.

  3. Open Settings and put the address in OTel Export: http://localhost:4318. Nothing is queued and nothing is sent while that field is empty.

  4. Confirm the disclosure the first time. It appears on the first send, not when you save the address, so drive something first — a governed call, then up to the flush interval (5 seconds by default). Once per session per address.

  5. Drive something worth watching, then run OTel Export: Show Export Status from the command palette.

If your collector needs a token, store it in the SECRETS panel and name that entry in the Endpoint secret ref setting. The token itself never goes into settings, because settings travel with the workspace. Endpoint header name and Endpoint auth scheme cover a vendor endpoint whose header is the key itself (x-honeycomb-team, raw).

What travels, and what does not

Call names, capability ids, timings, failures with their reason, the actor and origin of each call, token counts, the event names on your include-list, and what each call touched — the file, dataset or model it named, including the model that answered an LLM call (gen_ai.request.model). Call inputs and outputs do not: the span shape the runtime hands out carries neither, and what a call touched is an id rather than the content behind it.

The one exception worth knowing: an event on the include-list travels with its own body, already redacted by the runtime, flattened into attributes and capped. That list is the Event names setting — comma-separated, ai.agent*, workflow.*, policy.evaluated by default. Narrow it if your collector leaves your machine, or clear the field to send calls only; agent token counts arrive through those events, so an empty list means traces without token numbers.

Limits

  • Desktop only. Reaching a collector needs the host's own network surface, which the browser build does not have. The status capability says so rather than failing silently.
  • JSON, not protobuf. The transport carries OTLP over HTTP as JSON. A collector configured for protobuf only will refuse the batch, and the status will tell you it did.
  • A model name is only as good as the record. It travels when the call itself named one (an LLM call reports the model that answered), and it is never taken from your configured model, because a fallback can answer with a different one.
  • The last batch of a session can be lost. There is no shutdown hook to flush on, so a window closed within a few seconds of a run may leave the final spans unsent. Lower the flush interval if that matters, or run the flush command.