Skip to content

Observability

The platform uses OpenTelemetry end-to-end. Every Node service, the Next.js web app, the Apollo Router, and the Traefik API gateway emit OTLP/gRPC to a single collector. Locally that's the .NET Aspire Dashboard; in Azure it's the Container Apps managed OTel agent, which forwards to Application Insights.

The shared instrumentation package is @repo/observability (packages/infrastructure/observability). All Node and Next.js services import from it; nothing should pull @opentelemetry/* packages in directly.

Goals

  • One way to instrument a service. Apps choose a preset, not individual instrumentations.
  • Every exception lands in App Insights, even when the surrounding trace is sampled out.
  • Trace context propagates end-to-end across the native app, Traefik, the Apollo Router, the subgraphs, and the legacy API.
  • Configuration lives in code; prod and local behave the same except for the collector endpoint.

Telemetry signals

SignalProducerLocal sinkProduction sink
TracesOTel SDK + auto-instrumentations (HTTP, Undici, GraphQL, Pino, Redis, …)Aspire DashboardApp Insights requests / dependencies
LogsPino → PinoInstrumentation (requires pino external in bundles) → NodeSDK Logs providerAspire DashboardApp Insights traces
ExceptionsrecordException() (BaseError, global handlers, Next.js error hook)Aspire DashboardApp Insights exceptions
MetricsNot emitted — the Container Apps agent doesn't accept OTel metrics today----

Component map

Solid arrows are local; dotted arrows are production.

Subpages

  • Instrumentation — how @repo/observability is wired, the preset model, and the exception pipeline.
  • Local Development — the Aspire Dashboard, where to look for traces/logs/exceptions, and troubleshooting.
  • Azure Container Apps — the managed OTel agent, per-app env vars, and per-env sampling ratios.