trace
Create a custom trace span with OpenTelemetry-compatible timing, attributes, and status tracking. When telemetry is disabled, this is a zero-cost passthrough that calls the function with a no-op span.
Import
import { trace } from 'catmint/telemetry'
Signature
async function trace<T>(
name: string,
fn: (span: Span) => Promise<T> | T,
options?: SpanOptions,
): Promise<T>
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The span name, typically a dotted identifier like 'checkout.calculate'. |
fn | (span: Span) => Promise<T> | T | Yes | The function to execute within the span. Receives the active Span object. |
options | SpanOptions | No | Optional span configuration. |
SpanOptions
| Property | Type | Required | Description |
|---|---|---|---|
attributes | Record<string, string | number | boolean> | No | Key-value attributes attached to the span. |
kind | 'internal' | 'server' | 'client' | 'producer' | 'consumer' | No | The span kind. Defaults to 'internal'. |
Span
The Span interface passed to the callback:
interface Span {
setAttribute(key: string, value: string | number | boolean): void
addEvent(name: string, attributes?: Record<string, unknown>): void
setStatus(status: { code: number; message?: string }): void
end(): void
}
Use SpanStatusCode constants for the code field:
import { SpanStatusCode } from 'catmint/telemetry'
SpanStatusCode.OK // 1
SpanStatusCode.ERROR // 2
SpanStatusCode.UNSET // 0
Return Value
Returns the value returned by fn. If fn throws, the span is automatically marked with ERROR status and an exception event before re-throwing.
Behavior
- Telemetry disabled:
fnis called directly with a no-op span. Zero overhead. - Telemetry enabled: A real span is created with trace/span IDs, timing, and parent correlation.
- Sampling: If
sampleRate < 1in the telemetry config, some traces are skipped (using a no-op span). - Nesting: Spans automatically inherit the
traceIdfrom the parent span. A span stack tracks the active parent.
Examples
import { trace } from 'catmint/telemetry'
const result = await trace('checkout.calculate', async (span) => {
span.setAttribute('cart.items', cart.items.length)
const total = await calculateTotal(cart)
span.setAttribute('cart.total', total)
return total
})
// Nested spans share the same traceId
await trace('order.process', async (outerSpan) => {
outerSpan.setAttribute('order.id', orderId)
await trace('order.validate', async (innerSpan) => {
// innerSpan.traceId === outerSpan.traceId
await validateOrder(order)
})
await trace('order.charge', async (innerSpan) => {
innerSpan.setAttribute('payment.method', 'card')
await chargePayment(order)
})
})
// With span kind and initial attributes
await trace(
'api.fetchUser',
async (span) => {
const user = await fetch('/api/user')
return user.json()
},
{
kind: 'client',
attributes: { 'http.method': 'GET', 'http.url': '/api/user' },
},
)
See Also
- logger
- defineConfig (telemetry configuration)
