fix(telemetry): send browser events to the configured collector and keep logs off the anonymous one

Merged
WA
7d ago
+113-54

Summary

  • The browser telemetry route (/api/telemetry) read only TELEMETRY_ENDPOINT. A deployment that exports to its own collector through OTEL_EXPORTER_OTLP_ENDPOINT (with auth in OTEL_EXPORTER_OTLP_HEADERS) still posted browser events to the anonymous default, without auth, and each call waited out a 5s timeout when that host was unreachable.
  • The route ignored NEXT_TELEMETRY_DISABLED, so Docker and Helm installs, which set it by default, still forwarded browser events.
  • Its spans had no traceId/spanId and used the retired instrumentationLibrarySpans field, which current collectors drop.
  • Server-side, an install on the anonymous default also exported all server logs and metrics there, beyond the traces the privacy notice in telemetry.config.ts describes.

Changes:

  • lib/monitoring/otlp.ts: resolveOtlpTarget() (OTEL_EXPORTER_OTLP_ENDPOINT → TELEMETRY_ENDPOINT → configured fallback, plus OTLP headers), isTelemetryDisabled(), the shared normalizeOtlpTracesUrl, and the ANONYMOUS_TELEMETRY_ENDPOINT constant.
  • Browser events are unauthenticated input, so they never travel with the operator's collector credentials: the route sends them to TELEMETRY_ENDPOINT or the configured anonymous collector (telemetry.config.ts), returns forwarded: false without sending when telemetry is disabled, and emits valid OTLP JSON (scopeSpans, 32-hex trace id, 16-hex span id).
  • resolveOtlpTarget() never sends OTEL_EXPORTER_OTLP_HEADERS to the anonymous collector, recognizes it by host however the URL is written, and the traces URL is built on the parsed path (query strings kept, trailing slash accepted).
  • instrumentation-node.ts exports logs and metrics only to a collector the operator configured; on the anonymous endpoint it exports traces only. Explicit empty metricReaders/logRecordProcessors lists, because NodeSDK otherwise builds default OTLP exporters from env.
  • Self-hosting docs updated for TELEMETRY_ENDPOINT.

Type of Change

  • Bug fix

Testing

End to end against a real OpenTelemetry Collector (0.114.0, debug exporter), driving the actual POST handler:

  • With TELEMETRY_ENDPOINT pointing at the collector and an operator collector + Authorization header also configured: forwarded: true, posted to /v1/traces on TELEMETRY_ENDPOINT with no Authorization header, the operator collector never contacted; the collector decoded the span (page_view.view, valid trace/span ids).
  • With NEXT_TELEMETRY_DISABLED=1: forwarded: false, nothing sent.
  • The earlier version of the script against current staging failed every enabled-path check (it sent nowhere configured).

Plus biome, check:utils, check:comment-hygiene; type-check and the full audit suite run in CI.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing (new tests pass the test-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)