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 "@usestratus/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 "@usestratus/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);
  }
}

Exporting Traces

Register trace processors to export every completed trace:

trace-processor.ts
import { addTraceProcessor, withTrace } from "@usestratus/sdk/core";

addTraceProcessor({
  async exportTrace(trace) {
    await fetch("https://telemetry.example.com/traces", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(trace),
    });
  },
});

await withTrace("support_request", async () => {
  return run(agent, "Help this customer");
});

Processor failures are caught and logged to console.warn so telemetry outages don't fail agent runs.

FunctionDescription
addTraceProcessor(processor)Append a processor
setTraceProcessors(processors)Replace all processors
clearTraceProcessors()Remove all processors

Azure Monitor

Use the built-in Azure Monitor exporter to send trace and span events to Application Insights:

azure-monitor.ts
import {
  addTraceProcessor,
  createAzureMonitorTraceExporter,
  withTrace,
} from "@usestratus/sdk/core";

addTraceProcessor(
  createAzureMonitorTraceExporter({
    connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING,
    serviceName: "support-agent",
  }),
);

const { result, trace } = await withTrace("support_request", async () => {
  return run(agent, "Look up order 123");
});

If connectionString is omitted, the exporter reads APPLICATIONINSIGHTS_CONNECTION_STRING. If serviceName is omitted, it uses OTEL_SERVICE_NAME and falls back to "stratus-agent".

The exporter emits Application Insights event envelopes:

EventDescription
stratus.traceOne event per trace with trace name and total duration
stratus.spanOne event per span with span name, type, duration, trace ID, and parent span ID

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