Recording tracer
RecordingTracer is the test counterpart to
OtelTracerAdapter.
Instead of exporting spans, it stores them in memory —
tests assert on the recorded spans.
import { TestKit, RecordingTracer, TracingExtensionId } from 'actor-ts';
const tracer = new RecordingTracer();const tk = TestKit.create();tk.system.extension(TracingExtensionId).configure({ tracer });
// ... run the actor ...ref.tell({ kind: 'process' });await probe.expectMsg(...);
// Assert on recorded spans:const spans = tracer.recordedSpans();expect(spans).toHaveLength(1);expect(spans[0].name).toBe('actor.receive');expect(spans[0].attributes['actor.path']).toContain('worker');What it records
Section titled “What it records”Each RecordedSpan:
interface RecordedSpan { name: string; context: SpanContext; parent?: SpanContext; attributes: Record<string, AttributeValue>; events: Array<{ name: string; attrs: ... }>; status: { status: 'ok' | 'error'; message?: string }; startTimeMs: number; endTimeMs: number; ended: boolean; kind: SpanKind;}You see what attributes were set, when the span started + ended, its kind, its parent (for verifying causality chains), and its final status.
The API
Section titled “The API”class RecordingTracer implements Tracer { // ... full Tracer interface ...
recordedSpans(): RecordedSpan[]; // all spans, including unended finishedSpans(): RecordedSpan[]; // spans where end() was called reset(): void; // clear recorded state}recordedSpans returns every span the tracer was asked to
create. finishedSpans filters to those that ended — useful to
detect “started a span and never ended it” bugs.
Common assertion patterns
Section titled “Common assertion patterns””Did the framework auto-span this actor’s message?"
Section titled “”Did the framework auto-span this actor’s message?"”ref.tell({ kind: 'work' });await probe.expectMsg(...);
const spans = tracer.finishedSpans();const receive = spans.find(s => s.name === 'actor.receive' && s.attributes['actor.path']?.includes('worker'));expect(receive).toBeDefined();expect(receive!.status.status).toBe('ok');"Did my custom span fire with the right attributes?"
Section titled “"Did my custom span fire with the right attributes?"”const orderSpan = tracer.finishedSpans().find(s => s.name === 'place-order');expect(orderSpan).toBeDefined();expect(orderSpan!.attributes['order.id']).toBe('o-1');expect(orderSpan!.attributes['order.amount']).toBe(42);"Did spans link up correctly?”
Section titled “"Did spans link up correctly?””const spans = tracer.finishedSpans();const parent = spans.find(s => s.name === 'http-request');const child = spans.find(s => s.name === 'db-query');
expect(child!.parent?.traceId).toBe(parent!.context.traceId);Useful for verifying trace causality — parent context correctly propagated to child spans.
Resetting between tests
Section titled “Resetting between tests”beforeEach(() => tracer.reset());Without reset, recorded spans accumulate across tests. Reset on each test to keep assertions on the current test’s spans only.
Performance
Section titled “Performance”Recording is cheap — appending to an array. But spans
accumulate in memory. For long test runs, periodically reset
to avoid memory growth.
When NOT to use it
Section titled “When NOT to use it”Where to next
Section titled “Where to next”- Tracer API — the interface this implements.
- OTel adapter — the production alternative.
- Actor tracing — what the framework auto-spans, observable via this tracer.
- TestKit — pair with the recording tracer for assertion-friendly tests.
The RecordingTracer
API reference covers the full surface.