Skip to content

Tracer & Span ​

The core tracing primitives. A Tracer creates and manages traces; a Span represents a timed segment of work within a trace.

createTracer ​

Create a new tracer instance.

ts
import { createTracer } from '@ereo/trace'

const tracer = createTracer({
  maxTraces: 200,           // Completed traces to keep (ring buffer, FIFO eviction)
  maxSpansPerTrace: 500,    // Max spans before capping
  minDuration: 0,           // Drop traces shorter than this (ms)
})

Parameters ​

OptionTypeDefaultDescription
maxTracesnumber200Ring buffer capacity for completed traces
maxSpansPerTracenumber500Max spans per trace before new spans are dropped
minDurationnumber0Traces shorter than this (ms) are discarded on finalization

Returns ​

Tracer - The tracer instance.

Tracer Interface ​

ts
interface Tracer {
  startTrace(name: string, layer: SpanLayer, metadata?: Partial<TraceMetadata>): Span
  startSpan(name: string, layer: SpanLayer): Span
  activeSpan(): Span | null
  withSpan<T>(name: string, layer: SpanLayer, fn: (span: Span) => T | Promise<T>): T | Promise<T>
  getTraces(): TraceData[]
  getTrace(id: TraceId): TraceData | undefined
  subscribe(cb: (event: TraceStreamEvent) => void): () => void
  mergeClientSpans(traceId: TraceId, spans: SpanData[]): void
}

startTrace ​

Create a new trace with a root span. This is the entry point for request tracing.

ts
const rootSpan = tracer.startTrace('GET /api/users', 'request', {
  origin: 'server',
  method: 'GET',
  pathname: '/api/users',
})

// ... handle request ...

rootSpan.end()

startSpan ​

Create a child span of the current active span. If no trace is active, creates an orphan trace.

ts
// Automatically attaches to the most recently started trace
const span = tracer.startSpan('db.query', 'database')
span.setAttribute('db.statement', 'SELECT * FROM users')
span.end()

TIP

Prefer parentSpan.child() over tracer.startSpan() when you have access to the parent span. The child() method uses closures to guarantee correct trace context, while startSpan() is best-effort and picks the most recently started trace.

withSpan ​

Execute a function within a span. The span is automatically ended on completion or error.

ts
// Sync
const result = tracer.withSpan('compute', 'custom', (span) => {
  span.setAttribute('items', 1000)
  return heavyComputation()
})

// Async
const data = await tracer.withSpan('fetch-data', 'data', async (span) => {
  const res = await fetch('/api/data')
  return res.json()
})

Errors are recorded on the span and re-thrown.

getTraces / getTrace ​

Retrieve completed traces from the ring buffer.

ts
const allTraces = tracer.getTraces()    // TraceData[] (oldest first)
const trace = tracer.getTrace(traceId)  // TraceData | undefined

subscribe ​

Subscribe to real-time trace lifecycle events. Returns an unsubscribe function.

ts
const unsubscribe = tracer.subscribe((event) => {
  switch (event.type) {
    case 'trace:start':  // New trace begun
    case 'trace:end':    // Trace finalized (all spans ended)
    case 'span:start':   // Span created
    case 'span:end':     // Span ended
    case 'span:event':   // Event recorded on a span
  }
})

// Later
unsubscribe()

mergeClientSpans ​

Merge browser-side spans into a completed server trace. Called automatically by the WebSocket transport when the client submits spans.

ts
tracer.mergeClientSpans(traceId, clientSpans)

Span Interface ​

ts
interface Span {
  readonly id: SpanId
  readonly traceId: TraceId
  setAttribute(key: string, value: string | number | boolean): void
  event(name: string, attributes?: Record<string, string | number | boolean>): void
  end(): void
  error(err: unknown): void
  child(name: string, layer: SpanLayer): Span
}

setAttribute ​

Set a key-value attribute on the span. No-op after end().

ts
span.setAttribute('http.method', 'GET')
span.setAttribute('db.row_count', 42)
span.setAttribute('cache.hit', true)

event ​

Record a timestamped event within the span. No-op after end().

ts
span.event('cache.get', { key: 'user:123', hit: true })
span.event('route.matched', { pattern: '/api/users/[id]' })

end ​

End the span and record the end time. Idempotent (calling multiple times has no effect).

ts
span.end()
span.end() // no-op, safe to call again

When all spans in a trace have ended, the trace is finalized and stored in the ring buffer.

error ​

Record an error on the span and set status to 'error'. Extracts message, name, and stack (truncated to 500 chars) from Error instances. For non-Error values, records String(err) as the message.

ts
span.error(new Error('Connection refused'))
// Sets: error.message, error.name, error.stack, status = 'error'

span.error('string error')
// Sets: error.message = 'string error', status = 'error'

child ​

Create a child span in the same trace. The child inherits the trace ID via closure, ensuring correct context even with concurrent requests.

ts
const dataSpan = rootSpan.child('data-loading', 'data')
const dbSpan = dataSpan.child('db.query', 'database')
dbSpan.end()
dataSpan.end()

Types ​

TraceData ​

ts
interface TraceData {
  id: TraceId                        // 32-char hex string
  rootSpanId: SpanId                 // ID of the root span
  startTime: number                  // performance.now() timestamp
  endTime: number
  duration: number                   // endTime - startTime (ms)
  spans: Map<SpanId, SpanData>       // All spans in this trace
  metadata: TraceMetadata
}

SpanData ​

ts
interface SpanData {
  id: SpanId                         // 16-char hex string
  traceId: TraceId
  parentId: SpanId | null            // null for root spans
  name: string
  layer: SpanLayer
  status: SpanStatus                 // 'ok' | 'error' | 'timeout'
  startTime: number
  endTime: number
  duration: number
  attributes: Record<string, string | number | boolean>
  events: SpanEvent[]
  children: SpanId[]
}

TraceMetadata ​

ts
interface TraceMetadata {
  origin: 'server' | 'client'
  method?: string                    // HTTP method
  pathname?: string                  // URL pathname
  statusCode?: number                // HTTP status code
  routePattern?: string              // Matched route pattern
}

TraceStreamEvent ​

ts
type TraceStreamEvent =
  | { type: 'trace:start'; trace: TraceData }
  | { type: 'trace:end'; trace: TraceData }
  | { type: 'span:start'; span: SpanData }
  | { type: 'span:end'; span: SpanData }
  | { type: 'span:event'; spanId: SpanId; event: SpanEvent }

Released under the MIT License.