Skip to content

OpenTelemetry

EvalHub instruments the evaluation API and job-runtime sidecar with OpenTelemetry (OTEL). You can export traces, metrics, and logs to any OTLP-compatible collector (for example SigNoz, Jaeger, or an OpenShift Observability stack), and optionally scrape application metrics via Prometheus.

OTEL is implemented in the eval-hub server. The Python SDK and community adapters do not ship their own OTEL exporters; adapter container logs can still be forwarded through the server when job log export is enabled.

For low-level implementation details, see the developer doc in the server repo: OTEL.md.

ComponentTracesMetricsLogs
EvalHub API (eval-hub)YesYesOptional
Job runtime sidecarYes (when server OTEL is enabled)OTLP onlyVia shared exporter settings
Adapter containers (eval-hub-contrib)Optional export at job completion
evalhub-mcp
eval-runtime-init

Default OTEL service.name values:

  • API: github.com/eval-hub/eval-hub
  • Sidecar: github.com/eval-hub/eval-runtime-sidecar

Override with otel.service_name (config) or spec.otel.serviceName (EvalHub CR).

OTEL is off by default. Turn it on in server config for local/dev, or on the EvalHub custom resource for OpenShift.

otel:
enabled: true
exporter_type: "otlp-grpc" # otlp-grpc | otlp-http | stdout
exporter_endpoint: "localhost:4317"
exporter_insecure: false
sampling_ratio: 1.0
enable_tracing: true
enable_metrics: true # required for application metrics and OTEL-bridged /metrics
# enable_logs: true
# enable_job_container_logs: true # requires enable_logs; exports adapter logs at job completion
# metric_export_interval: 60s
# service_name: "custom-service-name"
prometheus:
enabled: true
port: 8081
FlagEffect
otel.enabledMaster switch; required for any OTEL export
otel.enable_tracingTracerProvider and trace export
otel.enable_metricsMeterProvider, application metrics, DB pool metrics, and OTEL-bridged Prometheus scrape
otel.enable_logsLoggerProvider; bridges application slog output to OTEL Logs
otel.enable_job_container_logsFetches adapter container logs when a job reaches a terminal state and emits them as OTEL log records (requires enable_logs)
prometheus.enabledDedicated /metrics port; with otel.enable_metrics, registers the OTEL Prometheus reader as a dual sink

When tracing is enabled, EvalHub exports spans over the configured OTLP (or stdout) exporter. Propagators are W3C Trace Context and Baggage.

API (inbound): every registered HTTP route gets an otelhttp span named {METHOD} {route_pattern}. Evaluation, collection, and provider handlers add child spans.

API (outbound): the MLflow HTTP client uses an instrumented transport.

Database: SQL queries produce client spans via otelsql when storage scans or metrics are enabled. Disable query spans with disable_database_otel_scan: true / disableDatabaseOtelScans: true (metrics-only mode can still use otelsql when metrics are on).

Sidecar: when OTEL is enabled on the server, settings are written into each job pod’s sidecar_config.json. The sidecar instruments inbound /health and proxy traffic, and outbound calls to EvalHub, MLflow, model endpoints, and OCI registries.

Application metrics require otel.enable_metrics (or spec.otel.enableMetrics).

OTEL nameTypical Prometheus nameNotes
http.server.request.durationhttp_server_request_duration_*Per-route duration
http.server.request.counthttp_server_request_countMethod, route, status; unmatched → http.route=not_found
http.server.active_requestshttp_server_active_requestsIn-flight requests
http.server.request.body.sizehttp_server_request_body_size_*Request body size
http.server.response.body.sizehttp_server_response_body_size_*Response body size

The /metrics scrape endpoint itself is not instrumented (avoids self-scrape noise). Cluster scraping uses the dedicated metrics server on port 8081; local mode also serves /metrics on the main API port.

OTEL nameWhen recordedAttributes
evalhub.evaluation_jobsJob created, cancelled, or runtime start failedaction = created | cancelled | runtime_start_failed; runtime on create/fail
evalhub.evaluation_job_completionsJob reaches a terminal statestate = completed | failed | cancelled | partially_failed
evalhub.benchmark_runtime_errorsRuntime fails to schedule/start a benchmarkruntime = kubernetes | local

There is no end-to-end job duration histogram and no per-tenant label on domain metrics yet.

When otelsql is active and metrics are enabled, EvalHub exports query timing (go.sql.query_timing) and connection-pool gauges/counters (go.sql.connections_*).

When both prometheus.enabled and otel.enable_metrics are true, the OTEL Prometheus exporter is registered as an additional metric reader. Scrape :8081/metrics (or the main API port in local mode). See also Server API.

With otel.enable_logs / enableLogs:

  1. Service logs — application structured logs are teed to the OTEL LoggerProvider (stdout JSON logs are preserved).
  2. Export — logs use the same exporter_type and endpoint as traces and metrics.

With otel.enable_job_container_logs / enableJobContainerLogs (API only; requires logs enabled):

  • When a job reaches a terminal state (completed, failed, partially_failed, cancelled), EvalHub asynchronously fetches adapter container logs (tail capped at 1000 lines) and emits each line as an OTEL log record with attributes such as evalhub.job.id, evalhub.benchmark.id, and evalhub.log.source=container.
  • Export runs in a background goroutine so workload callbacks are not blocked.
  • Cancelled jobs may delete runtime resources before logs are fetched; export is not triggered on per-benchmark events (only the overall job terminal transition).

This is how eval-hub-contrib adapter output becomes visible in your observability backend without instrumenting each adapter.

The eval-hub repository includes a Podman Compose stack for SigNoz under tests/otel/. Requires Podman and at least 4GB memory.

Terminal window
cd tests/otel
make start-signoz
EndpointURL
SigNoz UIhttp://localhost:3301
OTLP gRPClocalhost:4317
OTLP HTTPlocalhost:4318

Point a local EvalHub at SigNoz:

otel:
enabled: true
exporter_type: "otlp-grpc"
exporter_endpoint: "localhost:4317"
exporter_insecure: true
enable_tracing: true
enable_metrics: true
metric_export_interval: 10s

Start the API, generate traffic, then filter in SigNoz by service.name = github.com/eval-hub/eval-hub (or the sidecar name). Dual-sink Prometheus metrics remain on :8081/metrics.

Stop the stack with make stop-signoz from tests/otel.

ProjectRole in observability
eval-hubOTEL SDK bootstrap, HTTP/DB instrumentation, domain metrics, log bridge, job container log export
eval-hub-sdkAdapters report job phase/status via callbacks; those updates drive server-side job metrics and (when configured) terminal-state log export. The Python client itself does not export OTEL signals.
eval-hub-contribFramework adapters run in job pods; enable enable_job_container_logs to ship their stdout/stderr through EvalHub into your collector
  • Runtime job work is detached from the create-job HTTP trace; K8s client calls and eval-runtime-init are not traced.
  • evalhub-mcp has no OTEL instrumentation.
  • No job duration histogram, benchmark success counters (errors only), or metric exemplars yet.
  • Sidecar has no Prometheus dual-sink and no sidecar-specific domain metrics.
  • enable_job_container_logs has no effect unless enable_logs is true.

Full gap list: OTEL.md — Known gaps.