Skip to main content

Tracer API

The Tracer provides distributed tracing capabilities with span-based instrumentation, ideal for tracking execution flow across agents and capturing LLM request/response details.

Tracer Class

The Tracer manages trace context and creates spans for instrumentation.
class Tracer {
  constructor(traceId?: string);
  startSpan(name: string, opts?: { parent?: Span; taskId?: string; agentId?: string }): Span;
  propagationContext(span: Span): { traceId: string; parentSpanId: string };
  getTraceId(): string;
  static fromPropagated(ctx: { traceId: string; parentSpanId: string }): Tracer;
}

Constructor

traceId
string
Optional trace ID. If not provided, a new UUID will be generated

Methods

startSpan

Create and start a new span.
startSpan(
  name: string,
  opts?: { parent?: Span; taskId?: string; agentId?: string }
): Span
name
string
required
Name of the span (e.g., “execute-task”, “llm-request”)
opts.parent
Span
Parent span for creating child spans
opts.taskId
string
Task ID to associate with this span
opts.agentId
string
Agent ID to associate with this span
Returns: A new Span instance that automatically emits a “begin” event. Example:
const tracer = createTracer();
const span = tracer.startSpan("execute-task", {
  taskId: "task-123",
  agentId: "worker-1"
});

propagationContext

Serialize trace context for passing to child processes or remote agents.
propagationContext(span: Span): { traceId: string; parentSpanId: string }
span
Span
required
Span to serialize context from
Returns: Object containing traceId and parentSpanId for propagation. Example:
const ctx = tracer.propagationContext(span);
// Send ctx to child process or remote agent

getTraceId

Get the trace ID for this tracer.
getTraceId(): string
Returns: The trace ID (UUID format).

fromPropagated (static)

Recreate a Tracer from a propagated context (e.g., from a parent process).
static fromPropagated(ctx: { traceId: string; parentSpanId: string }): Tracer
ctx.traceId
string
required
Trace ID from parent context
ctx.parentSpanId
string
required
Parent span ID from parent context
Returns: A new Tracer with the propagated trace ID. Example:
// In child process:
const tracer = Tracer.fromPropagated({
  traceId: parentCtx.traceId,
  parentSpanId: parentCtx.parentSpanId
});

Span Class

Represents a single unit of work being traced.
class Span {
  readonly spanId: string;
  
  event(name: string, attrs?: Record<string, string | number | boolean>): void;
  setStatus(status: "ok" | "error", message?: string): void;
  setAttribute(key: string, value: string | number | boolean): void;
  setAttributes(attrs: Record<string, string | number | boolean>): void;
  child(name: string, overrides?: { taskId?: string; agentId?: string }): Span;
  end(): void;
  context(): TraceContext;
}

Properties

spanId
string
Unique identifier for this span (16-character hex string)

Methods

event

Emit an instant event within the span.
event(name: string, attrs?: Record<string, string | number | boolean>): void
name
string
required
Event name
attrs
Record<string, string | number | boolean>
Optional attributes to attach to the event
Example:
span.event("checkpoint-reached", { step: 3, progress: 0.75 });

setStatus

Set the final status of the span.
setStatus(status: "ok" | "error", message?: string): void
status
string
required
Span status: “ok” or “error”
message
string
Optional status message (typically used for errors)
Example:
span.setStatus("error", "Task execution failed");
span.end();

setAttribute

Set a single attribute on the span.
setAttribute(key: string, value: string | number | boolean): void
key
string
required
Attribute key
value
string | number | boolean
required
Attribute value
Example:
span.setAttribute("http.status_code", 200);
span.setAttribute("http.method", "POST");

setAttributes

Set multiple attributes at once.
setAttributes(attrs: Record<string, string | number | boolean>): void
attrs
Record<string, string | number | boolean>
required
Object containing key-value pairs to set as attributes
Example:
span.setAttributes({
  "http.status_code": 200,
  "http.method": "POST",
  "response.size": 1024
});

child

Create a child span parented to this span.
child(name: string, overrides?: { taskId?: string; agentId?: string }): Span
name
string
required
Name for the child span
overrides.taskId
string
Override task ID (inherits from parent if not specified)
overrides.agentId
string
Override agent ID (inherits from parent if not specified)
Returns: A new child Span. Example:
const parentSpan = tracer.startSpan("process-task");
const childSpan = parentSpan.child("read-file");
// Do work...
childSpan.end();
parentSpan.end();

end

End the span and emit the final “end” event with computed duration.
end(): void
Always call end() when the span’s work is complete. This method is idempotent—subsequent calls will emit a warning but won’t error.
Example:
const span = tracer.startSpan("operation");
try {
  // Do work...
  span.setStatus("ok");
} catch (err) {
  span.setStatus("error", String(err));
} finally {
  span.end();
}

context

Get the trace context for this span.
context(): TraceContext
Returns: TraceContext object containing traceId, spanId, and parentSpanId.

Functions

createTracer

Factory function to create a new Tracer instance.
function createTracer(traceId?: string): Tracer
traceId
string
Optional trace ID. If not provided, a new UUID will be generated
Returns: A new Tracer instance. Example:
import { createTracer } from "@longshot/core";

const tracer = createTracer();
const span = tracer.startSpan("main-operation");

enableTracing

Enable trace file output. Creates timestamped NDJSON files for traces and LLM details in <projectRoot>/logs/.
function enableTracing(projectRoot: string): { traceFile: string; llmDetailFile: string }
projectRoot
string
required
Absolute path to the project root directory
Returns: Object containing paths to both trace and LLM detail files. Example:
import { enableTracing } from "@longshot/core";

const { traceFile, llmDetailFile } = enableTracing("/path/to/project");
console.log(`Traces: ${traceFile}`);
console.log(`LLM Details: ${llmDetailFile}`);
// Output:
// Traces: /path/to/project/logs/trace-2026-03-03T10-30-45.ndjson
// LLM Details: /path/to/project/logs/llm-detail-2026-03-03T10-30-45.ndjson
Call this function once at startup. Subsequent calls are no-ops and return the existing file paths.

closeTracing

Close trace file streams. Call on graceful shutdown.
function closeTracing(): void
Example:
import { closeTracing } from "@longshot/core";

process.on("SIGTERM", () => {
  closeTracing();
  process.exit(0);
});

writeLLMDetail

Write full LLM request/response detail to the LLM detail log, correlated with a span.
function writeLLMDetail(
  spanId: string,
  data: { messages: unknown[]; response?: unknown; error?: string }
): void
spanId
string
required
Span ID to correlate with this LLM detail entry
data.messages
unknown[]
required
Array of messages sent to the LLM
data.response
unknown
LLM response object
data.error
string
Error message if the request failed
Example:
import { writeLLMDetail } from "@longshot/core";

const span = tracer.startSpan("llm-request");

try {
  const messages = [{ role: "user", content: "Hello" }];
  const response = await callLLM(messages);
  
  writeLLMDetail(span.spanId, { messages, response });
  span.setStatus("ok");
} catch (err) {
  writeLLMDetail(span.spanId, { messages, error: String(err) });
  span.setStatus("error");
} finally {
  span.end();
}

Types

TraceContext

interface TraceContext {
  traceId: string;
  spanId: string;
  parentSpanId?: string;
}

SpanEvent

interface SpanEvent {
  timestamp: number;
  trace: TraceContext;
  spanName: string;
  spanKind: "begin" | "end" | "event";
  spanStatus?: "ok" | "error";
  durationMs?: number;
  attributes?: Record<string, string | number | boolean>;
  taskId?: string;
  agentId?: string;
}

LLMDetailEntry

interface LLMDetailEntry {
  timestamp: number;
  spanId: string;
  messages: unknown[];
  response?: unknown;
  error?: string;
}

Output Format

Trace File Format

Span events are written as NDJSON:
{"timestamp":1709467845000,"trace":{"traceId":"550e8400-e29b-41d4-a716-446655440000","spanId":"a1b2c3d4e5f6g7h8"},"spanName":"execute-task","spanKind":"begin","taskId":"task-123","agentId":"worker-1"}
{"timestamp":1709467850000,"trace":{"traceId":"550e8400-e29b-41d4-a716-446655440000","spanId":"a1b2c3d4e5f6g7h8"},"spanName":"execute-task","spanKind":"end","spanStatus":"ok","durationMs":5000,"taskId":"task-123","agentId":"worker-1"}

LLM Detail File Format

{"timestamp":1709467845000,"spanId":"a1b2c3d4e5f6g7h8","messages":[{"role":"user","content":"Hello"}],"response":{"role":"assistant","content":"Hi there!"}}

Complete Example

import { createTracer, enableTracing, closeTracing, writeLLMDetail } from "@longshot/core";

// Initialize tracing at startup
const { traceFile, llmDetailFile } = enableTracing("/path/to/project");
console.log(`Tracing to: ${traceFile}`);

// Create a tracer
const tracer = createTracer();

// Start a parent span
const parentSpan = tracer.startSpan("process-task", {
  taskId: "task-123",
  agentId: "worker-1"
});

parentSpan.setAttribute("task.priority", 1);

try {
  // Create child span for file reading
  const readSpan = parentSpan.child("read-files");
  readSpan.setAttribute("file.count", 3);
  // Do work...
  readSpan.setStatus("ok");
  readSpan.end();
  
  // Create child span for LLM request
  const llmSpan = parentSpan.child("llm-request");
  const messages = [{ role: "user", content: "Analyze this code" }];
  
  try {
    const response = await callLLM(messages);
    writeLLMDetail(llmSpan.spanId, { messages, response });
    llmSpan.setStatus("ok");
  } catch (err) {
    writeLLMDetail(llmSpan.spanId, { messages, error: String(err) });
    llmSpan.setStatus("error", String(err));
  } finally {
    llmSpan.end();
  }
  
  parentSpan.event("task-checkpoint", { step: 2 });
  parentSpan.setStatus("ok");
} catch (err) {
  parentSpan.setStatus("error", String(err));
} finally {
  parentSpan.end();
}

// Cleanup on shutdown
process.on("SIGTERM", () => {
  closeTracing();
  process.exit(0);
});

Best Practices

Always End Spans

Always call span.end() when work is complete, even in error cases. Use try-finally blocks to ensure cleanup.

Use Child Spans

Create child spans for nested operations to build a hierarchical trace that shows the full execution flow.

Capture LLM Details

Use writeLLMDetail() to capture full request/response data for debugging and analysis, separate from the main trace file.

Set Meaningful Attributes

Add attributes to spans to provide context. Use consistent naming (e.g., http.status_code, file.path).

Propagate Context

When spawning child processes or remote agents, use propagationContext() and fromPropagated() to maintain trace continuity.

Build docs developers (and LLMs) love