8f125d2a90
## Summary First implementation step of ADR-0018. Create [`src/environments/environment.ts`](apps/portal-shell/src/environments/environment.ts) holding the two SPA per-environment values the ADR calls out — `bffApiBaseUrl` and `otlpEndpoint` — and replace the hard-coded URLs at the two SPA call sites that needed them. ## What changes - **New `environment.ts`** with dev defaults (`http://localhost:3000/api` and `http://localhost:4318/v1/traces`). Header comment links to ADR-0018, documents the constraint that per-environment siblings must share the same shape, and notes that nothing here is a secret (the SPA bundle is public). - **`observability/tracing.ts`** reads `environment.otlpEndpoint` for the exporter, and **derives** the `propagateTraceHeaderCorsUrls` regex from `environment.bffApiBaseUrl` — a future change to the BFF origin propagates `traceparent` to the right host automatically, no second edit needed. - **`home-status.service.ts`** builds `/health` as `${environment.bffApiBaseUrl}/health`. ## What this PR explicitly does NOT do - **No `environment.staging.ts` / `environment.prod.ts`** yet. The ADR says "ship later" for those, and the real prod / staging URLs are unknown until the infrastructure ADR lands. Dropping plausible-but-wrong URLs into the repo would be worse than waiting. - **No `fileReplacements` configuration in `project.json`** — it depends on the per-environment files existing. Wired in the same PR that introduces them. - **No BFF-side audit pool split** (`AUDIT_DATABASE_URL` validator, second Prisma client, boot-time UPDATE-rejection self-test). Also in the ADR's Confirmation list, but it touches `AuditModule` and deserves its own review. Separate PR. - **No `SERVICE_VERSION` wiring** in `tracing.ts`. Still hard-coded to `'dev'`; the build-time version source (same one that will feed the footer's dev-only version badge) is its own small chantier. ## Test plan - [x] `pnpm exec nx run-many -t lint test build --projects=portal-shell` — green (36 / 36 specs unchanged, no new tests needed). - [x] Production build size unchanged (121 kB gzip initial — `environment.ts` is one literal object inlined by the bundler). - [ ] Manual: `pnpm exec nx serve portal-shell` → home page loads, the health widget hits the BFF, Jaeger shows the SPA `document_load` + `fetch` + BFF child span trace. - [ ] Manual: temporarily change `bffApiBaseUrl` to `http://localhost:9999/api` → the fetch fails (expected), and the `traceparent` propagation regex no longer matches `:3000` (verifiable in Network panel — header is absent on cross-origin requests). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #90
88 lines
3.9 KiB
TypeScript
88 lines
3.9 KiB
TypeScript
/**
|
|
* OpenTelemetry SDK bootstrap for the SPA (per ADR-0012, phase 2).
|
|
*
|
|
* Symmetrical to apps/portal-bff/src/observability/tracing.ts — must
|
|
* be the very first import in `main.ts` so the auto-instrumentations
|
|
* patch `fetch` / `XMLHttpRequest` / `addEventListener` before any
|
|
* application code runs.
|
|
*
|
|
* What this enables out of the box
|
|
* ────────────────────────────────
|
|
* - `document_load` span on first paint, capturing every Performance
|
|
* Timing API metric (DNS, TLS, request, response, DOM events).
|
|
* - `fetch` spans for every outgoing request, with the W3C
|
|
* `traceparent` header propagated so the BFF picks up the same
|
|
* trace id and produces a child span. The result in Jaeger is one
|
|
* end-to-end trace SPA → BFF → DB.
|
|
* - `user_interaction` spans for click / keypress events on
|
|
* instrumented elements.
|
|
*
|
|
* Notes on the zoneless setup
|
|
* ───────────────────────────
|
|
* Angular here is zoneless (per ADR-0004), so we deliberately do NOT
|
|
* pull in `@opentelemetry/context-zone`. The default
|
|
* `StackContextManager` baked into `@opentelemetry/sdk-trace-web` is
|
|
* sufficient: the auto-instrumentations capture context at patch
|
|
* time and propagate it across the boundaries they own (fetch
|
|
* lifecycle, event handlers). Custom spans across `await` will need
|
|
* explicit `context.with(...)` plumbing — fine, encountered as code
|
|
* lands.
|
|
*
|
|
* Endpoint configuration
|
|
* ──────────────────────
|
|
* The OTLP/HTTP endpoint and the BFF origin both come from
|
|
* `src/environments/environment.ts` (per ADR-0018). Per-environment
|
|
* siblings of that file ship under `fileReplacements`.
|
|
*/
|
|
|
|
import { WebTracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace-web';
|
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
|
|
import { registerInstrumentations } from '@opentelemetry/instrumentation';
|
|
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
|
|
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
|
|
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
|
|
|
|
import { environment } from '../environments/environment';
|
|
|
|
const SERVICE_NAME = 'portal-shell';
|
|
// Static for now; a follow-up wires this to the build-time version
|
|
// (same source as the footer's dev-only version badge).
|
|
const SERVICE_VERSION = 'dev';
|
|
|
|
// Derive the trace-header propagation pattern from the BFF base URL
|
|
// so a deploy-time change to `bffApiBaseUrl` automatically propagates
|
|
// `traceparent` to the right origin. RegExp special chars are escaped
|
|
// before going into the source.
|
|
const bffOrigin = new URL(environment.bffApiBaseUrl).origin;
|
|
const bffOriginRegex = new RegExp(`^${bffOrigin.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/.*`);
|
|
|
|
const provider = new WebTracerProvider({
|
|
resource: resourceFromAttributes({
|
|
[ATTR_SERVICE_NAME]: SERVICE_NAME,
|
|
[ATTR_SERVICE_VERSION]: SERVICE_VERSION,
|
|
}),
|
|
spanProcessors: [
|
|
new BatchSpanProcessor(new OTLPTraceExporter({ url: environment.otlpEndpoint })),
|
|
],
|
|
});
|
|
|
|
provider.register();
|
|
|
|
registerInstrumentations({
|
|
instrumentations: [
|
|
// Times the initial page load — fires once.
|
|
new DocumentLoadInstrumentation(),
|
|
// Wraps every `fetch` call. Propagates `traceparent` to the BFF
|
|
// origin derived from the environment.
|
|
new FetchInstrumentation({
|
|
propagateTraceHeaderCorsUrls: [bffOriginRegex],
|
|
}),
|
|
// Auto-spans on click / keypress / submit (and a small allow-list
|
|
// beyond that). Quiet in dev, useful in staging perf
|
|
// investigations.
|
|
new UserInteractionInstrumentation(),
|
|
],
|
|
});
|