Documentation Index Fetch the complete documentation index at: https://docs.sedata-ai.tech/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 ()
}
})
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.