Tracers
Infrastructure

Tracers

Distributed tracing adapters

A tracer wraps each send in a tracing span. withTracing accepts any object that implements the TracerLike interface — structurally compatible with OpenTelemetry's Tracer, so an OTel tracer can be passed directly with no adapter.

When to use a tracer

Use a tracer when you need to understand how a notification send fits into a larger request. Tracers are the right tool when:

  • Distributed tracing — you already have OpenTelemetry or a similar tracing system and want notification sends to appear as spans alongside your API handlers, database calls, and queue processing.
  • Latency debugging — you need to see exactly how long each send took and where the time went (middleware, render, transport) in the context of the parent request.
  • Correlation — you want to link a notification send back to the HTTP request or background job that triggered it through trace and span IDs.

If you only need a flat record of "this send happened" without parent-child context, a sink is simpler. Tracers add value when the notification is one step in a larger operation and you need to see the whole picture.

The TracerLike interface

Prop

Type

The SpanLike interface

Prop

Type

OpenTelemetry

Pass an OTel tracer directly — no wrapper needed:

import { trace } from '@opentelemetry/api';
import { withTracing } from '@betternotify/core/middlewares';

withTracing({ tracer: trace.getTracer('betternotify') });

inMemoryTracer

For tests, use inMemoryTracer to record spans and assert on them:

import { inMemoryTracer } from '@betternotify/core/tracers';

const tracer = inMemoryTracer();

// after sending:
console.log(tracer.spans.length);
console.log(tracer.spans[0].name);
console.log(tracer.spans[0].attributes);
console.log(tracer.spans[0].status);

Each recorded span has name, attributes, status, exceptions, and ended.

On this page