Askr
Server & APIs

OpenTelemetry

Use opentelemetry with the current published Askr packages.

  • @askrjs/otel0.0.2

How to use opentelemetry

Create the function-first telemetry bridge after installing an application-owned OpenTelemetry provider and wrap work at semantic boundaries.

  1. Import the published @askrjs/otel entrypoint.
  2. Keep opentelemetry configuration next to the component, route, or server composition root that owns it.
  3. Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { createTelemetry } from '@askrjs/otel';

const telemetry = createTelemetry({ tracerName: 'project-api' });
return telemetry.request({ requestId }, () => router.handle(request));

Instrumentation boundary

`@askrjs/otel` only talks to the standard `@opentelemetry/api` peer dependency — it never bundles an SDK, span processor, exporter, or vendor backend. Call `createTelemetry(options)` once during app composition, and every span or log call routes through whatever provider your app registers; without one, the no-op implementation from the standard API absorbs the calls silently. That keeps this package usable in a test suite or a small app with zero telemetry infrastructure attached.

Spans and fields

The returned `Telemetry` object exposes one helper per lifecycle stage — `request`, `routeMatch`, `loader`, `action`, `apiOperation`, `queryPrefetch`, `ssrRender`, `viteDocument` — each wrapping a unit of work and recording it under a fixed `askr.*` `TelemetryOperation` name. `span(operation, fields, work)` is the general form underneath all of them if you need a custom operation. `TelemetryFields` is a narrow, typed shape — `requestId`, `traceId`, `route`, `action`, `status`, `durationMs` — so there's no open-ended bag of attributes to accidentally overload a span with.

Redaction

Because `TelemetryFields` is a closed interface rather than `Record<string, unknown>`, there's no field for request bodies, submitted form values, cookies, tokens, or arbitrary user attributes — you literally cannot pass them in through the typed API. The `route` field is meant to hold a route pattern like `/projects/:projectId`, not the raw, potentially sensitive URL a user actually requested. This is what keeps span data safe to forward to third-party backends by default, without a separate scrubbing pass.

Exporter setup

Choosing and configuring an actual OpenTelemetry SDK, processor, and exporter is application work, done in your composition root alongside `createTelemetry`. `TelemetryOptions` lets you customize the tracer's `tracerName`/`tracerVersion`, plug in a `logger` callback to mirror span-level events into your own structured logs, and override `now` for deterministic timing in tests. `extract`, `inject`, and `withContext` round out the object for propagating trace context across process or transport boundaries once a real exporter is wired up.