Skip to content

Instrumentors ​

Built-in instrumentors for all 11 framework layers. Each instrumentor creates either a child span (for timed operations) or an event (for lightweight annotations).

Request (Layer 1) ​

The request middleware creates the root trace span wrapping the entire request lifecycle. It must be the first middleware in your stack.

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

server.use(traceMiddleware(tracer, {
  exclude: ['/_ereo/', '/__ereo/', '/favicon.ico'],
  recordHeaders: false,
}))

Options ​

OptionTypeDefaultDescription
excludestring[]['/_ereo/', '/__ereo/', '/favicon.ico']Path prefixes to skip
recordHeadersbooleanfalseRecord request headers (skips cookie/authorization)

Behavior ​

  • Creates root span: {method} {pathname} (layer: request)
  • Sets attributes: http.method, http.pathname, http.search, http.status_code
  • Attaches tracer + active span to request context via setTracer() / setActiveSpan()
  • Reads X-Ereo-Trace-Id from request headers (client correlation)
  • Injects X-Ereo-Trace-Id into response headers
  • On error: records error, sets http.status_code = 500, re-throws

Routing (Layer 2) ​

ts
import { traceRouteMatch, recordRouteMatch } from '@ereo/trace'

traceRouteMatch ​

Wraps route matching in a child span. Records route pattern, ID, params, and layouts.

ts
const match = traceRouteMatch(rootSpan, () => {
  return router.match(pathname)
})

If the match function returns null or an object without a route property, records a 404 event.

recordRouteMatch ​

Lighter-weight alternative that records a match as an event on the parent span.

ts
// Matched route
recordRouteMatch(rootSpan, {
  route: { id: 'blog-slug', path: '/blog/[slug]' },
  params: { slug: 'hello' },
})

// No match (404)
recordRouteMatch(rootSpan, null)

Data / Loaders (Layer 3) ​

ts
import { traceLoader, recordLoaderMetrics, traceCacheOperation } from '@ereo/trace'

traceLoader ​

Wraps a single loader in a child span. Handles sync and async functions.

ts
const users = await traceLoader(rootSpan, 'users', async () => {
  return db.user.findMany()
})

Creates span: loader:{key} with attribute loader.key.

recordLoaderMetrics ​

Creates child spans from pipeline execution metrics. Call after pipeline.execute().

ts
recordLoaderMetrics(rootSpan, [
  { key: 'user', duration: 12.1, cacheHit: false, source: 'db' },
  { key: 'posts', duration: 18.3, cacheHit: true },
  { key: 'comments', duration: 8.0, error: 'timeout' },
])

LoaderTraceInfo ​

ts
interface LoaderTraceInfo {
  key: string
  duration: number
  cacheHit?: boolean
  source?: string
  waitingFor?: string[]
  error?: string
}

traceCacheOperation ​

Records a cache event on the parent span.

ts
traceCacheOperation(rootSpan, 'get', 'user:123', true)   // cache hit
traceCacheOperation(rootSpan, 'set', 'user:123')          // cache write
traceCacheOperation(rootSpan, 'invalidate', 'user:*')     // cache invalidation

Keys longer than 100 characters are truncated.

Forms (Layer 4) ​

ts
import { traceFormSubmit, recordFormValidation } from '@ereo/trace'

traceFormSubmit ​

Wraps form submission in a child span. Handles sync and async.

ts
const result = await traceFormSubmit(rootSpan, 'checkout', async () => {
  return processOrder(formData)
}, { fieldCount: 8 })

Creates span: form:{name} with attributes form.name and optional form.field_count.

recordFormValidation ​

Records validation timing as an event.

ts
recordFormValidation(rootSpan, 'checkout', {
  errorCount: 2,
  validationMs: 5.3,
  errorSources: ['sync', 'schema'],
})

Signals (Layer 5) ​

ts
import { recordSignalUpdate, recordSignalBatch } from '@ereo/trace'

recordSignalUpdate ​

Records a signal update event on the active span.

ts
recordSignalUpdate(span, 'count', { subscriberCount: 3, batched: false })

recordSignalBatch ​

Records a batch of signal updates.

ts
recordSignalBatch(span, ['count', 'total', 'items'], { subscriberCount: 8 })

RPC (Layer 6) ​

ts
import { traceRPCCall, recordRPCValidation } from '@ereo/trace'

traceRPCCall ​

Wraps an RPC procedure call in a child span.

ts
const result = await traceRPCCall(rootSpan, 'users.list', 'query', async () => {
  return db.user.findMany()
})

Creates span: rpc:{procedure} with attributes rpc.procedure and rpc.type.

recordRPCValidation ​

Records input validation timing as an event.

ts
recordRPCValidation(rootSpan, 'users.create', 1.5, true)

Database (Layer 7) ​

ts
import { tracedAdapter, traceQuery } from '@ereo/trace'

tracedAdapter ​

Wraps a database adapter using a Proxy. Instruments query, execute, get, all, and run methods with automatic span creation.

ts
const db = tracedAdapter(rawAdapter, () => getActiveSpan(ctx))

// All query methods are now instrumented
const users = await db.query('SELECT * FROM users WHERE role = ?', ['admin'])

Each instrumented call creates span: db.{method} with attributes:

  • db.operation - Method name
  • db.statement - SQL (first 200 chars)
  • db.param_count - Number of parameters
  • db.row_count - Result array length (if applicable)

Non-database methods pass through untouched. If getSpan() returns undefined, the original method is called without tracing.

traceQuery ​

Manual instrumentation for individual queries.

ts
const posts = await traceQuery(rootSpan, 'select', 'SELECT * FROM posts', async () => {
  return rawAdapter.query('SELECT * FROM posts')
})

Auth (Layer 8) ​

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

traceAuthCheck ​

Wraps an auth check in a child span. Records success/failure.

ts
const session = await traceAuthCheck(rootSpan, 'requireAuth', async () => {
  return verifySession(request)
}, { provider: 'jwt', roles: ['admin'] })

Creates span: auth:{operation} with attributes:

  • auth.operation - 'requireAuth' | 'optionalAuth' | 'requireRoles' | 'custom'
  • auth.provider - Auth provider name
  • auth.roles - Required roles (comma-separated)
  • auth.result - 'ok' or 'denied'
  • auth.redirect - Redirect URL (if error is a Response with Location header)

Islands / Hydration (Layer 9) ​

ts
import { traceHydration, recordHydration } from '@ereo/trace'

traceHydration ​

Wraps island hydration in a child span.

ts
await traceHydration(rootSpan, 'Counter', 'idle', async () => {
  await hydrateComponent(Counter, props)
}, { propsSize: 256 })

Creates span: hydrate:{component} with attributes island.component, island.strategy, and optional island.props_size.

Strategies: 'load' | 'idle' | 'visible' | 'media' | 'none'

recordHydration ​

Lighter-weight event recording.

ts
recordHydration(rootSpan, 'Sidebar', 'visible', 25.5)

Build (Layer 10) ​

ts
import { traceBuild, traceBuildStage } from '@ereo/trace'

traceBuild ​

Creates a root trace for the build process.

ts
const buildSpan = traceBuild(tracer, 'production build')

traceBuildStage ​

Wraps a build stage in a child span.

ts
await traceBuildStage(buildSpan, 'route-discovery', async () => {
  return discoverRoutes('./src/routes')
}, { filesCount: 42 })

await traceBuildStage(buildSpan, 'bundle', async () => {
  return bundle({ target: 'browser' })
})

buildSpan.end()

Errors (Layer 11) ​

ts
import { traceError, withErrorCapture } from '@ereo/trace'

traceError ​

Records an error on a span with phase information.

ts
traceError(span, error, 'loader')

Records:

  • Calls span.error(err) (sets status, error.message, error.name, error.stack)
  • Sets error.phase attribute
  • For Error instances: sets error.class and emits an 'error' event

Phases: 'middleware' | 'loader' | 'action' | 'render' | 'rpc' | 'unknown'

withErrorCapture ​

Wraps a function with automatic error capture. Errors are recorded on the span and re-thrown.

ts
// Sync
const html = withErrorCapture(span, 'render', () => {
  return renderToString(<App />)
})

// Async
const data = await withErrorCapture(span, 'loader', async () => {
  return fetchData()
})

Released under the MIT License.