Skip to main content

Documentation Index

Fetch the complete documentation index at: https://sedataai.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

instrumentServer returns an ObservabilityInstance with helpers for the three OTel signals you’ll most often need: spans, histograms, and counters.
const telemetry = instrumentServer(server, telemetryConfig)

Custom spans

Use startActiveSpan for any operation you want timed and traced. The session id is added automatically.
telemetry.startActiveSpan(
  'cache.lookup',
  { 'cache.key': 'user:42' },
  (span) => {
    const value = cache.get('user:42')
    span.setAttribute('cache.hit', !!value)
    span.end()
    return value
  },
)
Always call span.end() inside the callback. The helper does not auto-end.

Async spans

await telemetry.startActiveSpan('db.query', { 'db.statement': 'SELECT ...' }, async (span) => {
  try {
    const rows = await db.query('SELECT ...')
    span.setAttribute('db.rows', rows.length)
    return rows
  } catch (err) {
    span.recordException(err as Error)
    span.setStatus({ code: 2, message: (err as Error).message }) // ERROR
    throw err
  } finally {
    span.end()
  }
})

Nested under a tool span

If you call startActiveSpan from inside a tool handler, the new span becomes a child of the auto-generated tools/call <toolName> span — you’ll see the nesting in your trace UI.

Histograms

Use a histogram for distributions you care about — durations, sizes, scores.
const recordLookup = telemetry.getHistogram('cache.lookup.duration', {
  description: 'Cache lookup duration',
  unit: 'ms',
})

const t0 = Date.now()
const value = cache.get(key)
recordLookup(Date.now() - t0, { 'cache.hit': !!value })
getHistogram returns a recorder function — keep it around (don’t recreate per call) to avoid allocating a new instrument on every record.

Counters

For monotonically increasing counts:
const incRetry = telemetry.getIncrementCounter('upstream.retry.count', {
  description: 'Upstream retry count',
})

incRetry(1, { upstream: 'github', reason: 'timeout' })

Process attributes through your data processors

If you’ve configured dataProcessors, every attribute set passes through them before it reaches the exporter. Useful for redacting:
const redactEmails = (data: Record<string, any>) => {
  for (const k of Object.keys(data)) {
    if (typeof data[k] === 'string' && /\S+@\S+/.test(data[k])) {
      data[k] = '[REDACTED:email]'
    }
  }
  return data
}

const config: TelemetryConfig = {
  // ...
  dataProcessors: [redactEmails],
}
You can also call telemetry.processTelemetryAttributes(data) directly if you need to apply your processors to a value outside the standard span/metric path.

Pattern: time a critical path

async function fetchUser(id: string) {
  return telemetry.startActiveSpan('user.fetch', { 'user.id': id }, async (span) => {
    const t0 = Date.now()
    try {
      const user = await db.user.findUnique({ where: { id } })
      span.setAttribute('user.found', !!user)
      return user
    } finally {
      span.setAttribute('user.fetch.duration_ms', Date.now() - t0)
      span.end()
    }
  })
}

Pattern: outcome counter

const outcome = telemetry.getIncrementCounter('agent.handoff.outcome', {
  description: 'Agent handoff outcome',
})

outcome(1, { from: 'router', to: 'researcher', status: 'success' })

Next

Automatic metrics

What’s already recorded for free.

Data processors

Mutate or redact attributes site-wide.