Skip to main content

Overview

The tracer is a singleton instance that manages the lifecycle of spans and traces in your application. It handles span creation, context propagation, buffering, and flushing to the ZeroEval backend. You typically don’t need to use the tracer directly—the @span decorator and helper functions provide a higher-level API. However, the tracer is useful for advanced use cases like manual span management.
import { tracer } from 'zeroeval';

const span = tracer.startSpan('my-operation');
try {
  // Your code here
  span.setIO({ query: 'hello' }, { result: 'world' });
} catch (error) {
  span.setError({ message: error.message });
} finally {
  tracer.endSpan(span);
}

Methods

startSpan()

Start a new span. If called within an existing span context, the new span will automatically become a child of the current span.
startSpan(
  name: string,
  opts?: {
    attributes?: Record<string, unknown>;
    sessionId?: string;
    sessionName?: string;
    tags?: Record<string, string>;
  }
): Span

Parameters

name
string
required
The name of the span (e.g., "generate-response", "database-query").
opts
object
Additional options for the span

Returns

span
Span
A new Span instance that you must end by calling tracer.endSpan(span).

Example

const span = tracer.startSpan('process-request', {
  attributes: { userId: '123' },
  tags: { env: 'production' }
});

try {
  // Your code
} finally {
  tracer.endSpan(span);
}

endSpan()

End a span and add it to the flush buffer. If the span hasn’t been ended manually, this method will call span.end() automatically.
endSpan(span: Span): void

Parameters

span
Span
required
The span to end.

Example

const span = tracer.startSpan('my-operation');
// ... do work ...
tracer.endSpan(span);

currentSpan()

Get the currently active span in the current async context, or undefined if there is no active span.
currentSpan(): Span | undefined

Returns

span
Span | undefined
The currently active span, or undefined if no span is active.

Example

const current = tracer.currentSpan();
if (current) {
  current.setIO({ input: 'data' });
}

flush()

Immediately flush all buffered spans to the ZeroEval backend. This is called automatically based on flushInterval and maxSpans configuration, but you can call it manually if needed.
flush(): Promise<void>

Returns

promise
Promise<void>
A promise that resolves when the flush completes. If the flush fails, spans are re-added to the buffer for retry.

Example

// Flush before shutting down
await tracer.flush();

configure()

Update tracer configuration after initialization. This is called automatically by init(), but you can use it to change settings at runtime.
configure(opts?: {
  flushInterval?: number;
  maxSpans?: number;
  collectCodeDetails?: boolean;
  integrations?: Record<string, boolean>;
}): void

Parameters

opts
object
Configuration options to update

Example

tracer.configure({
  flushInterval: 5,
  maxSpans: 50
});

shutdown()

Flush remaining spans and tear down integrations. This is called automatically on process exit (beforeExit, SIGINT, SIGTERM), but you can call it manually for graceful shutdown.
shutdown(): void

Example

process.on('SIGTERM', () => {
  tracer.shutdown();
  process.exit(0);
});

Context Propagation

The tracer uses Node.js AsyncLocalStorage to automatically propagate span context across async boundaries. This means child spans automatically inherit the parent span’s context without manual threading.
const parentSpan = tracer.startSpan('parent');

await someAsyncFunction(); // Child spans created here will inherit parent context

tracer.endSpan(parentSpan);

Buffering and Flushing

The tracer buffers completed spans and flushes them to the backend in batches:
  1. Time-based flushing: Spans are flushed every flushInterval seconds (default: 10s)
  2. Size-based flushing: Spans are flushed when the buffer reaches maxSpans (default: 100)
  3. Trace completion: Spans are buffered per trace until the root span completes, then moved to the main buffer
This batching approach minimizes network overhead while ensuring traces are sent as complete units.

Notes

  • The tracer is a singleton—there is one global instance per process
  • Spans are automatically ordered parent-first before flushing to ensure proper trace reconstruction
  • Failed flushes are retried by re-adding spans to the buffer
  • The tracer maintains separate buffers for active traces and completed spans

Build docs developers (and LLMs) love