Tracing
OpenTelemetry spans for performance debugging and operation tracking
Use ctx.tracer in agents and c.var.tracer in routes to create OpenTelemetry spans. Spans help you understand timing, track operations through your system, and debug performance issues.
Basic Span Pattern
Wrap operations in spans to track their duration and status:
import { createAgent } from '@agentuity/runtime';
import { SpanStatusCode } from '@opentelemetry/api';
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = createAgent('TracingExample', {
handler: async (ctx, input) => {
return ctx.tracer.startActiveSpan('generate-response', async (span) => {
try {
span.setAttribute('model', 'gpt-5-mini');
span.setAttribute('promptLength', input.prompt.length);
const { text, usage } = await generateText({
model: openai('gpt-5-mini'),
prompt: input.prompt,
});
span.setAttribute('outputLength', text.length);
span.setAttribute('totalTokens', usage.totalTokens);
span.setStatus({ code: SpanStatusCode.OK });
return { response: text };
} catch (error) {
span.setStatus({ code: SpanStatusCode.ERROR });
ctx.logger.error('Generation failed', error);
throw error;
}
});
},
});Spans automatically end when the handler completes. Always set the status to OK or ERROR.
Adding Context with Attributes
Use setAttribute to add searchable metadata to spans:
return ctx.tracer.startActiveSpan('user-lookup', async (span) => {
span.setAttribute('userId', input.userId);
span.setAttribute('source', 'api');
span.setAttribute('cached', false);
const user = await fetchUser(input.userId);
span.setAttribute('userFound', !!user);
span.setAttribute('accountType', user?.type ?? 'unknown');
span.setStatus({ code: SpanStatusCode.OK });
return user;
});Common attributes: IDs, counts, categories, boolean flags. These appear in trace views and can be filtered.
Recording Events
Use addEvent to mark significant moments within a span:
return ctx.tracer.startActiveSpan('data-pipeline', async (span) => {
span.addEvent('pipeline-started', { inputSize: data.length });
const validated = await validateData(data);
span.addEvent('validation-complete', { validRecords: validated.length });
const enriched = await enrichData(validated);
span.addEvent('enrichment-complete', { enrichedFields: 5 });
const stored = await storeData(enriched);
span.addEvent('storage-complete', { recordsStored: stored.count });
span.setStatus({ code: SpanStatusCode.OK });
return { processed: stored.count };
});Events create a timeline within the span, useful for understanding where time is spent.
Nested Spans
Create child spans for multi-step operations:
return ctx.tracer.startActiveSpan('rag-pipeline', async (parentSpan) => {
try {
parentSpan.setAttribute('query', input.query);
// Retrieve relevant context
const context = await ctx.tracer.startActiveSpan('retrieve-context', async (span) => {
span.setAttribute('index', 'knowledge-base');
const results = await ctx.vector.search('docs', { query: input.query, limit: 5 });
span.setAttribute('resultsFound', results.length);
span.setStatus({ code: SpanStatusCode.OK });
return results;
});
// Rerank results
const ranked = await ctx.tracer.startActiveSpan('rerank-results', async (span) => {
span.setAttribute('inputCount', context.length);
const reranked = await rerankByRelevance(context, input.query);
span.setAttribute('outputCount', reranked.length);
span.setStatus({ code: SpanStatusCode.OK });
return reranked;
});
// Generate answer
const answer = await ctx.tracer.startActiveSpan('generate-answer', async (span) => {
span.setAttribute('model', 'gpt-5-mini');
span.setAttribute('contextChunks', ranked.length);
const response = await generateWithContext(input.query, ranked);
span.setAttribute('responseLength', response.length);
span.setStatus({ code: SpanStatusCode.OK });
return response;
});
parentSpan.setStatus({ code: SpanStatusCode.OK });
return { answer, sources: ranked.map(r => r.id) };
} catch (error) {
parentSpan.setStatus({ code: SpanStatusCode.ERROR });
throw error;
}
});Nested spans create a parent-child hierarchy in trace views, showing how operations relate.
Tracing in Routes
Routes access the tracer via c.var.tracer:
import { createRouter } from '@agentuity/runtime';
import { SpanStatusCode } from '@opentelemetry/api';
const router = createRouter();
// import notificationSender from '@agent/notification-sender';
router.post('/customers/:id/notify', async (c) => {
return c.var.tracer.startActiveSpan('send-notification', async (span) => {
try {
const customerId = c.req.param('id');
span.setAttribute('customerId', customerId);
const body = await c.req.json();
span.setAttribute('notificationType', body.type);
span.setAttribute('channel', body.channel);
const result = await notificationSender.run({
customerId,
...body,
});
span.setAttribute('delivered', result.success);
span.setStatus({ code: SpanStatusCode.OK });
return c.json(result);
} catch (error) {
span.setStatus({ code: SpanStatusCode.ERROR });
throw error;
}
});
});
export default router;Viewing Traces
View traces for a session using the CLI:
# Get session details including trace timeline
agentuity cloud session get sess_abc123xyzTraces are also visible in the Agentuity Console, showing the full span hierarchy with timing. See CLI Reference for more trace viewing options.
When to Use Tracing
| Scenario | Approach |
|---|---|
| Simple operations | Logging is sufficient |
| Multi-step workflows | Create spans for each step |
| Performance debugging | Add spans to identify bottlenecks |
| External API calls | Wrap in spans to track latency |
| Agent-to-agent calls | Spans automatically propagate context |
Best Practices
- Name spans descriptively:
generate-summarynotstep-2 - Always set status:
SpanStatusCode.OKorSpanStatusCode.ERROR - Add relevant attributes: IDs, counts, and categories for filtering
- Use events for milestones: Mark significant points within long operations
- Keep spans focused: One span per logical operation
Next Steps
- Logging: Learn structured logging patterns
- Sessions & Debugging: Use session IDs for debugging
Need Help?
Join our Community for assistance or just to hang with other humans building agents.
Send us an email at hi@agentuity.com if you'd like to get in touch.
Please Follow us on
If you haven't already, please Signup for your free account now and start building your first agent!