Skip to content

Trace API

The Zelos Cloud Trace API provides a way to collect and analyze structured trace events from your application.

Quick Start

import zelos_sdk

zelos_sdk.init()

source = zelos_sdk.TraceSource("my_source")
source.log("my_event", {"my_data": 123.456})

Event Streaming Example

import time
import random
import zelos_sdk

zelos_sdk.init()

source = zelos_sdk.TraceSource("motor")

while True:
    source.log("stats", {
        "rpm": random.uniform(1000, 5000),
        "torque": random.uniform(10, 50),
        "battery_voltage": random.uniform(46, 54),
    })
    time.sleep(1)

Core Components

TraceSource

Represents a logical source of events within your application.

You can instantiate any number of TraceSource objects to help contextualize events.

Example: Tracing data from a motor subcomponent

import time
import zelos_sdk

zelos_sdk.init()

# Create a source with a name
source = zelos_sdk.TraceSource("motor")

# Log events on the source
source.log(...)

TraceWriter

The TraceWriter class enables capturing trace events to local files for offline analysis, debugging, or archival purposes. It provides a context manager interface for automatic resource management.

import time
import datetime
import zelos_sdk

# Initialize the SDK
zelos_sdk.init()

source = zelos_sdk.TraceSource("my_source")

# Prefix the trace file with the current time
now = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')

# Method 1: Using context manager
with zelos_sdk.TraceWriter(f"{now}_context_managed.trz") as writer:
    for i in range(10):
        source.log("sensor_data", {
            "temperature": 25.0,
            "humidity": 50.0,
        })
        time.sleep(0.1)

# Method 2: Using open/close
try:
    writer = zelos_sdk.TraceWriter(f"{now}_open_close.trz")
    writer.open()

    # Log events - they will be captured to the file
    for i in range(10):
        source.log("sensor_data", {
            "temperature": 25.0,
            "humidity": 50.0,
        })
        time.sleep(0.1)
finally:
    writer.close()

TraceSourceEvent

Create TraceSourceEvent objects with the TraceSource add_event method

TraceSourceEvent objects are also accessible through the corresponding source object's get_event method.

import time
import random
import zelos_sdk

zelos_sdk.init()

source = zelos_sdk.TraceSource("motor")

# Build your strongly-typed event with unit metadata
motor_stats = source.add_event("stats", [
    zelos_sdk.TraceEventFieldMetadata("rpm", zelos_sdk.DataType.Float64),
    zelos_sdk.TraceEventFieldMetadata("torque", zelos_sdk.DataType.Float64, "Nm"),
    zelos_sdk.TraceEventFieldMetadata("battery_voltage", zelos_sdk.DataType.Float32, "V"),
])

while True:
    motor_stats.log(
        rpm=random.uniform(1000, 5000),
        torque=random.uniform(10, 50),
        battery_voltage=random.uniform(46, 54),
    )
    time.sleep(1)

TracePublishClient

Manages connection to a trace ingestion service and handles batching and delivery of events.

# Create with default settings
client = zelos_sdk.TracePublishClient()

# Create with custom configuration
config = zelos_sdk.TracePublishClientConfig(
    url="grpc://localhost:2300",
)
client = zelos_sdk.TracePublishClient(config)

Advanced Usage

Enumeration, Value Table support

import time
from enum import IntEnum
import zelos_sdk

# Define your enumeration
class State(IntEnum):
    ON = 0
    OFF = 1

# Initialize the sdk
zelos_sdk.init()

# Create a source
source = zelos_sdk.TraceSource("my_device")

# Add an event with a state field
state_machine_event = source.add_event("state_machine", [
    zelos_sdk.TraceEventFieldMetadata("state", zelos_sdk.DataType.Int8)
])

# Add value table for the state field to map integer values to human-readable strings
source.add_value_table("state_machine", "state", {state.value: state.name for state in State})

def run_state_machine(current_state):
    match current_state:
        case State.ON:
            return State.OFF
        case State.OFF:
            return State.ON
        case _:
            raise ValueError("Unknown State!")

state = State.OFF
while 1:
    state = run_state_machine(state)
    state_machine_event.log(state=int(state))
    time.sleep(1)

Attribute style access

After defining an event, use attribute-style access for something a bit more Pythonic.

import time
import random
import zelos_sdk

zelos_sdk.init()

source = zelos_sdk.TraceSource("motor")

# Build your strongly-typed event with unit metadata
source.add_event("stats", [
    zelos_sdk.TraceEventFieldMetadata("rpm", zelos_sdk.DataType.Float64),
    zelos_sdk.TraceEventFieldMetadata("torque", zelos_sdk.DataType.Float64, "Nm"),
    zelos_sdk.TraceEventFieldMetadata("battery_voltage", zelos_sdk.DataType.Float32, "V"),
])

# Add a submessage
source.add_event("stats/averages", [
    zelos_sdk.TraceEventFieldMetadata("rpm_avg", zelos_sdk.DataType.Float64),
    zelos_sdk.TraceEventFieldMetadata("torque_avg", zelos_sdk.DataType.Float64, "Nm"),
    zelos_sdk.TraceEventFieldMetadata("battery_voltage_avg", zelos_sdk.DataType.Float32, "V"),
])

while True:
    source.stats.log(
        rpm=random.uniform(1000, 5000),
        torque=random.uniform(10, 50),
        battery_voltage=random.uniform(46, 54),
    )

    # Note '/' replaced with '.' for submessages
    source.stats.averages.log(
        rpm_avg=random.uniform(1000, 5000),
        torque_avg=random.uniform(10, 50),
        battery_voltage_avg=random.uniform(46, 54),
    )
    time.sleep(1)

Initialization with Configuration

# Initialize with custom configuration
zelos_sdk.init(
    "my_application",
    client_config=zelos_sdk.TracePublishClientConfig(
        url="grpc://localhost:2300",
    ),
    log_level="debug"
)

Custom Timestamps

# log event with a specific timestamp (nanoseconds since epoch)
timestamp_ns = int(time.time_ns())
motor_stats.log_at(timestamp_ns,
    rpm=3250.5,
    torque=42.8,
    battery_voltage=48.2,
    motor_temp=62.3
)

Field Types

import time
import zelos_sdk

zelos_sdk.init()
source = zelos_sdk.TraceSource("sensor")

# Define event with all available field types
comprehensive_event = source.add_event("status", [
    # Boolean type
    zelos_sdk.TraceEventFieldMetadata("fault_detected", zelos_sdk.DataType.Boolean),

    # Signed integer types
    zelos_sdk.TraceEventFieldMetadata("temperature_offset", zelos_sdk.DataType.Int8, "celsius"),
    zelos_sdk.TraceEventFieldMetadata("pressure_delta", zelos_sdk.DataType.Int16, "Pa"),
    zelos_sdk.TraceEventFieldMetadata("error_code", zelos_sdk.DataType.Int32),
    zelos_sdk.TraceEventFieldMetadata("timestamp_offset", zelos_sdk.DataType.Int64, "ns"),

    # Unsigned integer types
    zelos_sdk.TraceEventFieldMetadata("sensor_id", zelos_sdk.DataType.UInt8),
    zelos_sdk.TraceEventFieldMetadata("sample_count", zelos_sdk.DataType.UInt16),
    zelos_sdk.TraceEventFieldMetadata("total_readings", zelos_sdk.DataType.UInt32),
    zelos_sdk.TraceEventFieldMetadata("device_serial", zelos_sdk.DataType.UInt64),

    # Floating point types
    zelos_sdk.TraceEventFieldMetadata("temperature", zelos_sdk.DataType.Float32, "celsius"),
    zelos_sdk.TraceEventFieldMetadata("pressure", zelos_sdk.DataType.Float64, "Pa"),
    zelos_sdk.TraceEventFieldMetadata("voltage", zelos_sdk.DataType.Float64, "V"),
    zelos_sdk.TraceEventFieldMetadata("current", zelos_sdk.DataType.Float64, "A"),

    # Timestamp
    zelos_sdk.TraceEventFieldMetadata("measurement_time", zelos_sdk.DataType.TimestampNs),

    # String and binary data
    zelos_sdk.TraceEventFieldMetadata("device_name", zelos_sdk.DataType.String),
    zelos_sdk.TraceEventFieldMetadata("calibration_data", zelos_sdk.DataType.Binary),
])

Mock data

import time
import zelos_sdk

zelos_sdk.init()

mocks = [
    zelos_sdk.mock.MockBmsSource(),
    zelos_sdk.mock.MockSensorSource(),
    zelos_sdk.mock.MockInverterSource(),
    zelos_sdk.mock.MockDcdcSource(),
]

while 1:
    for mock in mocks:
        mock.generate()
    time.sleep(0.1)

Example: Logging Thermal Zones on Linux

Linux supports reading temperature sensors via sysfs. On a linux system, install uv and save the file below as temp_linux.py and run uv run temp_linux.py to start streaming data live. uv run --script <script_name>.py

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.10"
# dependencies = [ "zelos-sdk" ]
# ///
from pathlib import Path
import time

import zelos_sdk

zelos_sdk.init()

# Find all of our thermal zones from sysfs
thermal_zones = list(Path("/sys/class/thermal").glob("thermal_zone*"))

# Once per second, read the zone info and log it
src = zelos_sdk.TraceSource("linux_thermal")
while True:
    for zone_path in thermal_zones:
        # Read the zone info from sysfs
        zone_type = (zone_path / "type").read_text().strip()
        zone_temp_c = int((zone_path / "temp").read_text().strip()) / 1000.0

        # Log to the zelos sdk
        src.log_dict(zone_path.name, {"type": zone_type, "temp_c": zone_temp_c})

    time.sleep(1)