Skip to content

How to Read Trace Files

Python Only

The following APIs are only available in the python API

Read .trz trace files for offline analysis, debugging, or post-processing.

PyArrow

The TraceReader API returns data as PyArrow tables. Use pyarrow.compute for analysis operations.

Basic Reading

Use TraceReader to query metadata and data from trace files:

import zelos_sdk
import pyarrow as pa

# Read trace file using context manager
with zelos_sdk.TraceReader("output.trz") as reader:
    # Get time range
    time_range = reader.time_range()
    print(f"Trace from {time_range.start} to {time_range.end}")

    # List data segments
    segments = reader.list_data_segments()
    print(f"Found {len(segments)} data segments")

    # Query data (field format: */source/event.field)
    result = reader.query(
        data_segment_ids=[s.id for s in segments],
        fields=["*/experiment/measurement.value"],
        start=time_range.start,
        end=time_range.end,
    )

    # Convert to PyArrow table
    arrow_reader = pa.ipc.open_stream(result.to_arrow())
    table = arrow_reader.read_all()
    print(f"Retrieved {table.num_rows} rows with columns: {table.column_names}")

Reading Methods

with zelos_sdk.TraceReader("data.trz") as reader:
    segments = reader.list_data_segments()
    # File automatically closed

Manual Control

reader = zelos_sdk.TraceReader("data.trz")
try:
    reader.open()
    segments = reader.list_data_segments()
finally:
    reader.close()  # Always close!

Common Patterns

Field Discovery

Discover available fields before querying:

import zelos_sdk
import pyarrow as pa

# 1. Open trace file for reading
with zelos_sdk.TraceReader("recording.trz") as reader:
    # 2. Discover available segments
    segments = reader.list_data_segments()
    assert len(segments) > 0

    # 3. Discover all available fields hierarchically
    sources = reader.list_fields()

    # Navigate hierarchy: source → event → field
    for source in sources:
        print(f"Source: {source.name}")
        for event in source.events:
            print(f"  Event: {event.name}")
            for field in event.fields:
                print(f"    Field: {field.name}{field.path}")

    # 4. Query discovered field
    can_source = next(s for s in sources if s.name == "can")
    speed_event = next(e for e in can_source.events if e.name == "VehicleSpeed")
    speed_field = next(f for f in speed_event.fields if f.name == "speed")

    time_range = reader.time_range()
    result = reader.query(
        data_segment_ids=[s.id for s in segments],
        fields=[speed_field.path],  # "*/can/VehicleSpeed.speed"
        start=time_range.start,
        end=time_range.end,
    )

    # Convert to Arrow table
    arrow_reader = pa.ipc.open_stream(result.to_arrow())
    table = arrow_reader.read_all()
    print(f"Retrieved {table.num_rows} rows")

Query and Analyze Data

Query trace data and perform analysis using PyArrow:

import zelos_sdk
import pyarrow as pa
import pyarrow.compute as pc

with zelos_sdk.TraceReader("trace.trz") as reader:
    segments = reader.list_data_segments()
    time_range = reader.time_range()

    # Query field data
    result = reader.query(
        data_segment_ids=[s.id for s in segments],
        fields=["*/sensor/temperature.value"],
        start=time_range.start,
        end=time_range.end
    )

    # Convert to Arrow table
    arrow_reader = pa.ipc.open_stream(result.to_arrow())
    table = arrow_reader.read_all()

    # Analyze with PyArrow
    temp_col = [c for c in table.column_names if "temperature" in c][0]
    values = table.column(temp_col)

    print(f"Mean: {pc.mean(values).as_py():.2f}")
    print(f"Min: {pc.min(values).as_py():.2f}")
    print(f"Max: {pc.max(values).as_py():.2f}")

Metadata Inspection

Query trace metadata without loading data:

import zelos_sdk

def inspect_trace(filename):
    """Display trace file information"""
    with zelos_sdk.TraceReader(filename) as reader:
        # Time coverage
        time_range = reader.time_range()
        print(f"Time Range: {time_range.start} to {time_range.end}")

        # Data segments
        segments = reader.list_data_segments()
        print(f"\nData Segments ({len(segments)}):")
        for seg in segments:
            print(f"  {seg.id}")
            print(f"    Producer: {seg.producer}")
            if seg.start_date:
                print(f"    Time: {seg.start_date} to {seg.end_date}")

        # Traces (if available)
        traces = reader.list_traces()
        if traces:
            print(f"\nTraces ({len(traces)}):")
            for trace in traces:
                print(f"  {trace.name}")
                print(f"    ID: {trace.id}")
                print(f"    Segments: {len(trace.data_segments)}")

# Usage
inspect_trace("session_20240115_143022.trz")

Basic Analysis

Analyze trace data using PyArrow:

import zelos_sdk
import pyarrow as pa
import pyarrow.compute as pc

def load_fields(filename, field_patterns):
    """
    Load fields from trace file as Arrow table.

    Args:
        filename: Path to .trz file
        field_patterns: List of field paths (e.g., ["source/event/field"])

    Returns:
        PyArrow Table with time_s and requested fields
    """
    with zelos_sdk.TraceReader(filename) as reader:
        time_range = reader.time_range()
        segments = reader.list_data_segments()

        # Query data
        result = reader.query(
            data_segment_ids=[s.id for s in segments],
            fields=field_patterns,
            start=time_range.start,
            end=time_range.end,
        )

        # Convert to Arrow table
        arrow_reader = pa.ipc.open_stream(result.to_arrow())
        table = arrow_reader.read_all()

        return table

# Usage
table = load_fields("test.trz", [
    "experiment/measurement/value",
    "experiment/measurement/index"
])

# Find the value column (includes UUID prefix)
value_col = [c for c in table.column_names if c.endswith('/experiment/measurement/value')][0]

# Compute statistics using PyArrow
column = table.column(value_col)
print(f"Mean: {pc.mean(column).as_py()}")
print(f"Max: {pc.max(column).as_py()}")
print(f"Min: {pc.min(column).as_py()}")
print(f"Rows: {table.num_rows}")

Note on Column Names: Arrow table columns use the full field path including the data segment ID (UUID). When you query with a wildcard pattern like */experiment/measurement/value, the resulting column name will be <uuid>/experiment/measurement/value. Use .endswith() to find the actual column name.

Time Range Filtering

Query specific time windows:

import zelos_sdk
import pyarrow as pa

def load_time_window(filename, start_time, end_time, fields):
    """Load data from a specific time range"""
    with zelos_sdk.TraceReader(filename) as reader:
        # Get segments in time range
        segments = reader.list_data_segments_in_time_range(start_time, end_time)

        if not segments:
            print("No data in specified time range")
            return None

        # Query the window
        result = reader.query(
            data_segment_ids=[s.id for s in segments],
            fields=fields,
            start=start_time,
            end=end_time,
        )

        # Convert to Arrow table
        arrow_reader = pa.ipc.open_stream(result.to_arrow())
        table = arrow_reader.read_all()
        return table

# Usage - query one hour of data
table = load_time_window(
    "long_recording.trz",
    start_time="2024-01-15T14:00:00Z",
    end_time="2024-01-15T15:00:00Z",
    fields=["*/sensor/temperature.value"]
)

Querying Enumeration Data

Query enum fields and convert integers to strings using stored value tables:

import zelos_sdk
import pyarrow as pa

with zelos_sdk.TraceReader("controller_log.trz") as reader:
    segments = reader.list_data_segments()
    time_range = reader.time_range()

    # Query enum field (returns integer values)
    result = reader.query(
        data_segment_ids=[s.id for s in segments],
        fields=["*/controller/state.status"],
        start=time_range.start,
        end=time_range.end,
    )

    # Extract integer values
    arrow_reader = pa.ipc.open_stream(result.to_arrow())
    table = arrow_reader.read_all()
    status_col = next(c for c in table.column_names if "status" in c)
    status_ints = table.column(status_col).to_pylist()  # [0, 1, 2]

    # Retrieve enum mapping from database and convert to strings
    status_map = reader.get_value_table(segments[0].id, "controller/state.status")
    if status_map:
        status_strs = [status_map[v] for v in status_ints]
        print(status_strs)  # ["IDLE", "RUNNING", "ERROR"]

See Also