OTLP tracing setup
Crewship emits OpenTelemetry GenAI spans for every LLM call viainternal/telemetry. Any OTLP-compatible backend (self-hosted or
managed) can receive those spans by setting two env vars on the
Crewship process — no code change required.
Prerequisites
- An OTLP/HTTP-capable tracing backend reachable from the Crewship process.
- Credentials supplied by your backend (typical formats: a bearer token, a Basic-auth pair, or an API-key header).
OTEL_EXPORTER_OTLP_*env vars supported by Crewship since theinternal/telemetrypackage was added.
Configure
Set the env vars on the Crewship process (.env.local for dev.sh,
systemd unit Environment= for prod, container -e for Docker):
OTEL_EXPORTER_OTLP_ENDPOINT is a base URL, so /v1/traces is appended
to whatever you set — including a project-scoped path. A backend documenting
/api/public/otel wants spans at /api/public/otel/v1/traces, and setting
the prefix is enough:
/v1/traces it is not doubled, so an endpoint
written out in full keeps working.
To send traces somewhere the base-URL rule would not reach — a collector
serving them off a non-standard path — set the signal-specific variable
instead. It is used exactly as written, and takes precedence:
crewship. The init logs one of:
OTel GenAI telemetry enabled traces_url=http://.../v1/traces→ working. This is the resolved URL, signal path included — if it is not where you expected spans to go, the configuration is wrong and this line says so before any span is dropped.telemetry init failed, falling back to noop tracer→ check endpoint reachability and that header values parse (no quotes, no newline in base64).
Smoke test
Verify endpoint + auth without waiting for an LLM call.curl needs the
resolved URL — the one the startup line prints as traces_url:
crewship-smoke service should
appear shortly after.
What you get
Crewship’sinternal/telemetry wires GenAI Semantic Convention
attributes on every LLM call (defined in internal/telemetry/spans.go):
gen_ai.system—anthropic,openai,ollamagen_ai.request.model— model identifiergen_ai.usage.input_tokens/output_tokens/cached_input_tokensgen_ai.usage.cache_creation_tokensgen_ai.cost.total_usd
spans.go:
crewship.agent.id,crewship.agent.typecrewship.crew.id,crewship.mission.idcrewship.tool.name,crewship.tool.args_hash,crewship.tool.side_effect
spans_routine.go):
crewship.routine.slug,crewship.routine.run_id,crewship.routine.pipeline_idcrewship.routine.step.id,crewship.routine.step.type,crewship.routine.step.attempt
Operational notes
- Same-host loopback avoids the reverse-proxy hop. If the backend
is on a different host, point the endpoint at its public DNS —
Crewship uses HTTPS automatically when the endpoint URL starts
https://. - Batching is 5 s / 2048 spans / 512 per batch (see
internal/telemetry/provider.go). Spans emitted shortly before a forced shutdown may drop; the shutdown hook flushes whatever is still queued. - No-op fallback — empty
OTEL_EXPORTER_OTLP_ENDPOINTkeeps the binary running without an observability stack, which is the local dev default. - Per-deployment isolation — if your backend supports per-project
credentials, give every deployment its own pair so dev/staging/prod
traces stay sorted. Only
OTEL_EXPORTER_OTLP_HEADERSneeds to change.