PtcRunner.TraceLog.Analyzer (PtcRunner v0.13.0)

Copy Markdown View Source

Offline analysis of trace log files.

Provides functions to load, filter, summarize, and visualize trace data captured by PtcRunner.TraceLog.

Example

events = TraceLog.Analyzer.load("trace.jsonl")
summary = TraceLog.Analyzer.summary(events)

# Filter to specific event types
llm_events = TraceLog.Analyzer.filter(events, type: "llm")

# Find slowest operations
slowest = TraceLog.Analyzer.slowest(events, 5)

# Print timeline
TraceLog.Analyzer.print_timeline(events)

Summary

Types

A trace tree node representing a trace file and its children.

Functions

Builds a span hierarchy tree from events.

Deletes all trace files in the tree.

Exports a trace tree to Chrome DevTools Trace Event format.

Filters events by various criteria.

Returns events as a formatted string timeline.

Returns the trace tree as a formatted string.

Returns a flat list of all file paths in the trace tree.

Loads events from a JSONL trace file.

Loads a trace file and recursively loads all child traces.

Prints an ASCII timeline visualization of events.

Prints an ASCII visualization of the trace tree hierarchy.

Returns the program sources from turn events, in load order (nil for turns with no program, e.g. parse-failure or budget-stop turns).

Returns the turn events for a single session/correlation id, in load order.

Groups turn events into per-session summaries, keyed by correlation id (the session_id for session-driven turns, the agent_id for SubAgent turns).

Returns the N slowest events by duration.

Creates a summary of the trace execution.

Returns all canonical turn events (event == "turn"), in load order.

Types

trace_tree()

@type trace_tree() :: %{
  path: String.t(),
  trace_id: String.t() | nil,
  events: [map()],
  summary: map(),
  children: [trace_tree()]
}

A trace tree node representing a trace file and its children.

Fields:

  • path: File path to this trace
  • trace_id: Unique trace identifier
  • events: Loaded events from this trace
  • summary: Summary statistics for this trace
  • children: List of child trace tree nodes

Functions

build_tree(events)

@spec build_tree([map()]) :: [map()]

Builds a span hierarchy tree from events.

Groups events by span_id and constructs parent-child relationships using parent_span_id.

Examples

tree = Analyzer.build_tree(events)
# Returns nested structure with children

delete_tree(tree)

@spec delete_tree(trace_tree()) :: {:ok, non_neg_integer()} | {:error, term()}

Deletes all trace files in the tree.

Returns {:ok, deleted_count} on success, {:error, reason} on failure.

Examples

{:ok, tree} = Analyzer.load_tree("parent.jsonl")
{:ok, 29} = Analyzer.delete_tree(tree)

export_chrome_trace(tree, output_path)

@spec export_chrome_trace(trace_tree(), String.t()) :: :ok | {:error, term()}

Exports a trace tree to Chrome DevTools Trace Event format.

The output can be opened in Chrome DevTools (Performance panel → Load profile) or at chrome://tracing for flame chart visualization.

Parameters

  • tree - A trace tree from load_tree/1
  • output_path - Path to write the JSON file (e.g., "trace.json")

Examples

{:ok, tree} = Analyzer.load_tree("rlm_trace.jsonl")
:ok = Analyzer.export_chrome_trace(tree, "rlm_trace.json")

# Then open Chrome DevTools → Performance → Load profile → select rlm_trace.json
# Or navigate to chrome://tracing and load the file

Visualization

The flame chart shows:

  • Horizontal axis: Time (wider = longer duration)
  • Vertical stacking: Nested calls (children below parents)
  • Colors: Different categories (turns, tools, pmap)

Click any span to see details including arguments and results.

filter(events, criteria)

@spec filter(
  [map()],
  keyword()
) :: [map()]

Filters events by various criteria.

Options

  • :type - Event type prefix (e.g., "llm", "tool", "run")
  • :span_id - Filter by span ID
  • :min_duration_ms - Minimum duration in milliseconds

Examples

# All LLM events
llm_events = Analyzer.filter(events, type: "llm")

# All events taking > 100ms
slow = Analyzer.filter(events, min_duration_ms: 100)

# Events in a specific span
span_events = Analyzer.filter(events, span_id: "abc123")

format_timeline(events)

@spec format_timeline([map()]) :: String.t()

Returns events as a formatted string timeline.

Like print_timeline/1 but returns a string instead of printing.

format_tree(tree)

@spec format_tree(trace_tree()) :: String.t()

Returns the trace tree as a formatted string.

Like print_tree/1 but returns a string instead of printing.

list_tree(map)

@spec list_tree(trace_tree()) :: [String.t()]

Returns a flat list of all file paths in the trace tree.

Useful for cleanup operations.

Examples

{:ok, tree} = Analyzer.load_tree("parent.jsonl")
paths = Analyzer.list_tree(tree)
#=> ["parent.jsonl", "child1.jsonl", "child2.jsonl", ...]

load(path)

@spec load(String.t()) :: [map()]

Loads events from a JSONL trace file.

Returns a list of event maps in chronological order.

Examples

events = Analyzer.load("trace.jsonl")
length(events)  #=> 42

load_tree(path, opts \\ [])

@spec load_tree(
  String.t(),
  keyword()
) :: {:ok, trace_tree()} | {:error, term()}

Loads a trace file and recursively loads all child traces.

Child traces are discovered from:

  • pmap.stop events with child_trace_ids metadata
  • tool.stop events with child_trace_id metadata

Returns a tree structure where each node contains:

  • path: File path
  • trace_id: Trace ID
  • events: Loaded events
  • summary: Execution summary
  • children: List of child trace trees

Examples

{:ok, tree} = Analyzer.load_tree("parent_trace.jsonl")
length(tree.children)  #=> 28

Options

  • :base_dir - Directory to search for child trace files (defaults to same directory as parent)
  • :_seen - Internal option for cycle detection (do not set manually)

programs(events)

@spec programs([map()]) :: [String.t() | nil]

Returns the program sources from turn events, in load order (nil for turns with no program, e.g. parse-failure or budget-stop turns).

session_turns(events, correlation_id)

@spec session_turns([map()], String.t()) :: [map()]

Returns the turn events for a single session/correlation id, in load order.

sessions(events)

@spec sessions([map()]) :: [map()]

Groups turn events into per-session summaries, keyed by correlation id (the session_id for session-driven turns, the agent_id for SubAgent turns).

Each summary reports the driver, total turns, committed/failed counts, and total tool calls — enough to answer "what did my previous sessions do, and where did they waste turns?" before drilling into session_turns/2.

slowest(events, n \\ 5)

@spec slowest([map()], pos_integer()) :: [map()]

Returns the N slowest events by duration.

Only includes events that have a duration_ms field (typically stop events).

Examples

slowest = Analyzer.slowest(events, 5)
Enum.map(slowest, & &1["event"])  #=> ["llm.stop", "tool.stop", ...]

summary(events)

@spec summary([map()]) :: map()

Creates a summary of the trace execution.

Extracts key metrics from the trace including duration, turns, token counts, and call counts for LLM and tool operations.

Examples

summary = Analyzer.summary(events)
summary.duration_ms  #=> 1234
summary.turns        #=> 3
summary.llm_calls    #=> 3
summary.tool_calls   #=> 5

turn_events(events)

@spec turn_events([map()]) :: [map()]

Returns all canonical turn events (event == "turn"), in load order.