PtcRunner.TraceLog.Analyzer (PtcRunner v0.7.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 N slowest events by duration.

Creates a summary of the trace execution.

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)

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