Observability
Coverage matrix
Console has the widest blast radius — it can delete an organisation and put the whole
platform into maintenance — and the least instrumentation.
Pipelines
The three datasets
The security logger is created with
protectAll = true
(apps/webapp/lib/axiom/server.ts:110) — a different redaction posture from the general
logger.
Worker dataset names are set in the Fly config: cona_temporal-workers and
cona_temporal-activity, with _staging suffixes in staging.
Security events
proxy.ts emits to the security dataset at four points:
Logs are flushed through
flushAllLoggers(event) in a finally block so a thrown handler
cannot lose them.
@cona/observability
The shared core, with a colocated test for every module:
Sampling
Deterministic, controlled by two environment variables (sampling.ts:3-4):
CONA_LOG_SAMPLING_ENABLEDCONA_LOG_SAMPLING_RULES— a map of rule name to rate
OpenTelemetry
@cona/opentelemetry wraps setup and holds the only @prisma/* import in the
repository — @prisma/instrumentation at src/dependencies.ts:10.
Workers export metrics straight to Axiom OTLP:
worker.ts:377-378 notes OTLP export is best-effort: the telemetry package absorbs
exporter failures so observability can never block shutdown.
Worker-side OTel wiring lives in telemetry.ts, temporal-opentelemetry.ts, and
temporal-metrics.ts.
Notes
Staging runs a failure probe.OBSERVABILITY_PROBE_ENABLED = 1 in staging, 0 in
production, driving observabilityFailureProbeWorkflow.
Browser logs take a detour. The client cannot write to Axiom directly, so
api/axiom/ingest forwards them. Portal has its own copy of this route.
Runtime context is split by environment. server-runtime-context.ts and
browser-runtime-context.ts in webapp, worker-runtime-context.ts in workers, all
feeding @cona/observability’s runtime-context.ts.
Error classification is deliberate. lib/axiom/request-error-classification.ts
decides which request failures are worth reporting — expected 4xx responses do not become
alerts.