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
@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 tracetrace_id: Unique trace identifierevents: Loaded events from this tracesummary: Summary statistics for this tracechildren: List of child trace tree nodes
Functions
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
@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)
@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 fromload_tree/1output_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 fileVisualization
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.
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")
Returns events as a formatted string timeline.
Like print_timeline/1 but returns a string instead of printing.
@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.
@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", ...]
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
@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.stopevents withchild_trace_idsmetadatatool.stopevents withchild_trace_idmetadata
Returns a tree structure where each node contains:
path: File pathtrace_id: Trace IDevents: Loaded eventssummary: Execution summarychildren: List of child trace trees
Examples
{:ok, tree} = Analyzer.load_tree("parent_trace.jsonl")
length(tree.children) #=> 28Options
: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)
@spec print_timeline([map()]) :: :ok
Prints an ASCII timeline visualization of events.
Shows the sequence of events with timing information.
Examples
Analyzer.print_timeline(events)
# Outputs:
# [0ms] run.start
# [10ms] llm.start
# [150ms] llm.stop (140ms)
# ...
@spec print_tree(trace_tree()) :: :ok
Prints an ASCII visualization of the trace tree hierarchy.
Shows execution times and nested structure for debugging and analysis.
Examples
{:ok, tree} = Analyzer.load_tree("parent.jsonl")
Analyzer.print_tree(tree)
# Output:
# ├─ [1234ms] parent (trace-abc123)
# │ ├─ [100ms] worker-1 (trace-def456)
# │ ├─ [120ms] worker-2 (trace-ghi789)
# │ └─ [95ms] worker-3 (trace-jkl012)
@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", ...]
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