stratus

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:

tracing.ts
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 spans

Trace Structure

types.ts
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:

types.ts
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 TypeWhat It Captures
model_callAn LLM API call (includes agent name, turn number, usage, tool call count)
tool_executionA tool's execute function (includes tool name)
handoffAn agent-to-agent handoff (includes from/to agent names)
guardrailGuardrail execution (input or output)
subagentA sub-agent execution (includes child agent name)
customCustom spans you create manually

Custom Spans

Access the current trace context to record your own spans:

custom-span.ts
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

inspect.ts
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

On this page