Jaeger
Jaeger is the original distributed-tracing UI from Uber. Modern versions (≥ 1.35) accept OTLP/HTTP natively — no collector needed in the middle.
Signals: traces only. Deployment: local (single container). Cost: OSS, no account needed.
Local setup
The plugin ships a single-container compose file:
cd ~/.hermes/plugins/hermes_otel
docker compose -f docker-compose/jaeger/docker-compose.yaml up -d
Then:
export OTEL_JAEGER_ENDPOINT="http://localhost:4318/v1/traces"
export OTEL_PROJECT_NAME="hermes-otel-jaeger"
UI at http://localhost:16686.
That file runs jaegertracing/all-in-one, the v1 line that ended with 1.76.0. For the
OpenTelemetry-Collector-based Jaeger v2 (what the Helm chart deploys) use
docker-compose/jaeger-v2/docker-compose.yaml: UI on http://localhost:16696, OTLP/HTTP on
http://localhost:4368/v1/traces. Export works the same. On the read side v2 serves only
/api/v3/... on the UI port (the classic /api/traces is 404, #245); the dashboard's
Jaeger adapter probes once per query URL (the classic /api/services first, which wins
when it answers, then /api/v3/services) and speaks whichever API the server has, so a
v1 and a v2 entry need the same two keys (endpoint, query_port). The classic API is
preferred because Jaeger 1.x's all-in-one also exposes an /api/v3 gateway, with
snake_case parameters that differ from v2's.
query_api: v1 or v3 on the entry pins it. Verified on 2.21.0 (2026-10-09): search,
filters, keyset paging and the trace detail all come from /api/v3/traces.
Multi-backend config
backends:
- type: jaeger
endpoint: http://localhost:4318/v1/traces
What you'll see
Jaeger shows the plugin's spans as a standard trace tree — the service-map view picks up the parent/child relationships between session, llm, api, and tool spans. Each span's attributes are rendered in the "Tags" panel (a long list with everything the plugin emitted).
Jaeger is not LLM-specific, so:
- Message content appears as the raw
input.value/gen_ai.content.prompttag rather than pretty-printed. - Token counts are tags, not first-class fields — look for
gen_ai.usage.input_tokens/llm.token_count.prompt. - Tool args / results are JSON in the
input.value/output.valuetags.
For LLM-native UI, pair Jaeger with a fan-out to Phoenix or Langfuse — see Multi-backend.
Metrics caveat
Jaeger is traces-only — no OTLP metrics ingest. The plugin auto-detects this and skips the metrics exporter when Jaeger is the only backend.
If you want the plugin's token / tool / cost metrics alongside Jaeger traces, route metrics to a Prometheus-compatible sink via a separate backend entry, or fan out to Phoenix / SigNoz in parallel.
Troubleshooting
"Spans are arriving but the service-map is empty"
- Jaeger builds the service-map from
service.nameon the resource. The plugin sets this fromOTEL_PROJECT_NAME. If it's unset, every span ends up on an "unknown-service" node — set the env var and restart.
"OTLP export 404s"
- Check your Jaeger version. OTLP ingest was added in 1.35. Older Jaegers need the separate
jaeger-collectorsidecar. The bundled compose uses a modern version.
"The path format looks wrong"
- Jaeger's OTLP endpoint is
/v1/traces(exactly like Phoenix, SigNoz, etc.). If you see/api/traces, you're looking at the query API, not the ingest API. The plugin writes to the ingest endpoint.