OpenTelemetry
This document describes how OpenTelemetry is wired in the API (apps/api) and what telemetry is emitted today.
Where setup happens
OpenTelemetry bootstrap lives in apps/api/src/shared/kernel/otel.ts via setupOtel(app).
apps/api/src/index.ts calls it early:
- creates Fastify app
- calls
setupOtel(app) - keeps returned
tracerProvider,meterProvider, andhttpRequestDurationMs - shuts both providers down during graceful shutdown
Tracing
Tracing is exported with OTLP HTTP/protobuf via OTLPTraceExporter.
- Exporter URL:
AXIOM_OTLP_TRACES_URL(default:https://api.axiom.co/v1/traces) - Headers:
Authorization: Bearer <AXIOM_EVENTS_TOKEN>X-Axiom-Dataset: <AXIOM_EVENTS_DATASET>
- Provider:
NodeTracerProvider - Span processor:
BatchSpanProcessor
Fastify request spans are created by @fastify/otel instrumentation plugin, registered in setupOtel.
Current instrumentation options:
ignorePaths: "/health"(health endpoint is excluded from instrumentation)
Metrics
Metrics are exported with OTLP HTTP/protobuf via OTLPMetricExporter.
- Exporter URL:
AXIOM_OTLP_METRICS_URL(default:https://api.axiom.co/v1/metrics) - Headers:
Authorization: Bearer <AXIOM_METRICS_TOKEN>x-axiom-metrics-dataset: <AXIOM_METRICS_DATASET>contentType: application/x-protobuf
Metric export is driven by PeriodicExportingMetricReader:
- export interval:
10_000ms - export timeout:
8_000ms
Custom metric emitted by API
The app creates one histogram:
- Name:
http.server.request.duration - Unit:
ms - Description: duration of incoming HTTP requests
Recorded in index.ts onResponse hook with attributes:
http.request.methodhttp.routehttp.response.status_code
OPTIONS requests are skipped in this hook, so those are not included in this custom histogram.
Resource attributes
Both tracer and meter providers share the same OpenTelemetry resource:
service.name = OTEL_SERVICE_NAME(fallback:kinky-api)
Request correlation and span enrichment
In index.ts:
onRequestresolves or createsrequestId(fromx-request-idheader orcrypto.randomUUID()).- The same ID is attached to the active span as
request.id. onResponseandsetErrorHandlerread current span context and includetrace_id/span_idin logs.
This gives practical trace-log correlation even though logs are sent by Pino separately.
Diagnostics (SDK internal logging)
If OTEL_LOG_LEVEL is set, setupOtel enables OpenTelemetry diagnostic logging using DiagConsoleLogger.
Supported values:
allverbosedebuginfowarnerrornone
If unset, OpenTelemetry diag logging is not explicitly enabled.
Required environment variables
Validated at startup in apps/api/src/shared/kernel/env.ts:
AXIOM_OTLP_TRACES_URLAXIOM_EVENTS_TOKENAXIOM_EVENTS_DATASETOTEL_SERVICE_NAMEAXIOM_OTLP_METRICS_URLAXIOM_METRICS_TOKENAXIOM_METRICS_DATASET
Operational notes
- If OTEL credentials/endpoints are invalid, exports will fail at runtime (the app still starts because providers are created regardless).
- Graceful shutdown calls
tracerProvider.shutdown()andmeterProvider.shutdown()to flush telemetry before process exit.