Tracing
Built-in span-based tracing for observability
Stratus includes an opt-in tracing system that records spans for model calls, tool executions, handoffs, subagents, and guardrails. Tracing uses AsyncLocalStorage for zero-overhead when inactive.
Basic Usage
Wrap your agent call with withTrace() to capture a trace:
import { withTrace, run, Agent } from "stratus-sdk/core";
const agent = new Agent({ name: "assistant", model, tools: [getWeather] });
const { result, trace } = await withTrace("weather_request", async () => {
return run(agent, "What's the weather in NYC?");
});
console.log(trace.name); // "weather_request"
console.log(trace.duration); // Total duration in ms
console.log(trace.spans); // Array of recorded spansTrace Structure
interface Trace {
id: string; // Unique trace ID
name: string; // Name passed to withTrace()
startTime: number; // Start timestamp
endTime?: number; // End timestamp
duration?: number; // Duration in ms
spans: Span[]; // Recorded spans
}Span Types
Each span captures a specific operation:
interface Span {
name: string;
type: "model_call" | "tool_execution" | "handoff" | "guardrail" | "subagent" | "custom";
startTime: number;
endTime: number;
duration: number;
metadata?: Record<string, unknown>;
children: Span[];
}| Span Type | What It Captures |
|---|---|
model_call | An LLM API call (includes agent name, turn number, usage, tool call count) |
tool_execution | A tool's execute function (includes tool name) |
handoff | An agent-to-agent handoff (includes from/to agent names) |
guardrail | Guardrail execution (input or output) |
subagent | A sub-agent execution (includes child agent name) |
custom | Custom spans you create manually |
Custom Spans
Access the current trace context to record your own spans:
import { getCurrentTrace } from "stratus-sdk/core";
const myTool = tool({
name: "search",
description: "Search docs",
parameters: z.object({ query: z.string() }),
execute: async (_ctx, { query }) => {
const trace = getCurrentTrace();
const span = trace?.startSpan("vector_search", "custom", { query });
try {
const results = await vectorSearch(query);
return JSON.stringify(results);
} finally {
if (span) trace?.endSpan(span);
}
},
});Inspecting Traces
const { result, trace } = await withTrace("my_trace", async () => {
return run(agent, "Hello");
});
for (const span of trace.spans) {
console.log(`${span.type}: ${span.name} (${span.duration}ms)`);
if (span.metadata) {
console.log(" metadata:", span.metadata);
}
}Zero Overhead
When withTrace() is not used, getCurrentTrace() returns undefined and all tracing code paths are skipped. There is no performance cost for tracing when it's not active.
Edit on GitHub
Last updated on