Skip to main content

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 answers 404 (#245). The dashboard's Jaeger adapter probes once per query URL, trying the classic /api/services first and then /api/v3/services, and speaks whichever API the server has. So a v1 and a v2 entry need the same two keys, endpoint and query_port. The classic API is tried first because Jaeger 1.x's all-in-one also exposes an /api/v3 gateway whose snake_case parameters differ from v2's. query_api: v1 or v3 on the entry skips the probe. 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
query_port: 16686 # dashboard read path: the UI / query port (16696 for the v2 stack)

Without query_port the dashboard reads the query API on 16686, the port the bundled v1 stack and most Jaeger installs use. Set it when your query API is elsewhere (16696 for the bundled v2 stack).

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 the agent root and its 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.input.messages tag 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.value tags.

For LLM-native UI, pair Jaeger with a fan-out to Phoenix or Langfuse — see Multi-backend.

Query over MCP​

Traceloop's opentelemetry-mcp server reads this stack with BACKEND_TYPE=jaeger BACKEND_URL=http://localhost:16686, so Hermes, Claude Code or Cursor can search the traces and sum token usage over MCP. All eleven tools were verified against a Hermes workload; Jaeger v2 does not work (its UI port serves only /api/v3). Setup, observed output and the caveats: Query your traces over MCP.

Metrics caveat​

Jaeger is traces-only — no OTLP metrics or logs ingest. The jaeger type turns metrics and logs off by default, so the plugin builds no metrics exporter for this entry.

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.name on the resource. The plugin sets it to hermes-agent (or the profile name with service_name_from_profile, or your resource_attributes.service.name), so every Hermes span lands on one service node. The map needs spans from more than one service to draw edges; a single Hermes process shows one node. OTEL_PROJECT_NAME sets openinference.project.name, not the service name.

"OTLP export 404s"

  • Check your Jaeger version. OTLP ingest was added in 1.35. Older Jaegers need the separate jaeger-collector sidecar. 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.