feat(portal-shell): wire spa-side opentelemetry tracing #72
Reference in New Issue
Block a user
Delete Branch "feat/portal-shell/observability-foundations"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Phase 2 of ADR-0012 — closes the loop SPA → BFF → DB. After this PR, a single user action (initial page load, click, form submit) produces one trace whose root span is owned by the SPA and whose child spans cover the BFF request, Postgres queries through Prisma, and (eventually) Redis / downstream-API hops.
What lands
Browser-side OTel libs (production deps):
@opentelemetry/sdk-trace-web— browser tracer + provider@opentelemetry/exporter-trace-otlp-http— OTLP/HTTP+JSON exporter@opentelemetry/instrumentation— auto-instrumentation runtime@opentelemetry/instrumentation-fetch—fetch+ W3Ctraceparentpropagation@opentelemetry/instrumentation-document-load— initial-paint timings@opentelemetry/instrumentation-user-interaction— click / keypress / submitNo
@opentelemetry/context-zone: the workspace is zoneless per ADR-0004; the defaultStackContextManagercovers the auto-instrumented paths. Custom spans acrossawaitwill need explicitcontext.with(...)plumbing — fine, encountered as code lands.Code:
apps/portal-shell/src/observability/tracing.ts—WebTracerProviderbootstrap. Documents the load-order constraint inline (same pattern as the BFF: must be the very first import ofmain.ts, otherwise auto-instrumentations miss everything imported above).apps/portal-shell/src/main.tsnow imports the tracing module as line 1.CORS plumbing for end-to-end trace propagation:
apps/portal-bff/src/main.ts) callsenableCorswith a minimal dev allowlist (http://localhost:4200) and explicit permission for the W3Ctraceparent/tracestateheaders. The full security-grade CORS (per-environment allowlists, helmet, cookie-session, CSRF) belongs to the future phase-2 security ADR — this PR adds the strict minimum for the SPA→BFF trace context to survive cross-origin pre-flight.infra/local/otel-collector.yaml) gains acorsblock on its OTLP/HTTP receiver so the browser's own OTLP POST clears its pre-flight.ADR-0012 §Confirmation rewritten: a new "Wired in the SPA foundation PR (phase 2)" block enumerates what landed here; the carry-over "Wired as features land" list drops the SPA-side SDK item and adds a follow-up note about the security-grade CORS.
Verification
Open http://localhost:4200 → a
document_loadtrace appears in http://localhost:16686 withservice.name=portal-shell. From DevTools, runfetch('http://localhost:3000/api/health').then(r => r.json())→ a fetch span appears with a child BFF span on the same trace.Test plan
document_loadspan visible in Jaeger UI for the SPA.traceparent(visible in Network tab) and produces a single end-to-end trace SPA → BFF in Jaeger.traceparent,tracestate, or thelocalhost:4318/v1/tracesPOST.