Skip to main content
Every @agent_task LLM calls, tool invocations, and task dependencies is recorded as a span automatically. No manual instrumentation required. Traces capture timing, inputs, outputs, and parent relationships so you can understand exactly what your agent did and why.
Run MOTUS_TRACING=1 python my_agent.py to enable detailed tracing with file export in a single environment variable.

Collection levels

LevelCapturesOverhead
disabledNothingNone
basic (default)Task names, timing, parent relationshipsMinimal
detailed+ full messages, tool arguments, model outputsHigher
MOTUS_TRACING=1 sets collection to detailed and enables file export. The basic level is always on by default it adds negligible overhead and gives you timing data for every run.

Environment variables

VariablePurposeDefault
MOTUS_TRACING1 enables detailed collection + file exportoff
MOTUS_COLLECTION_LEVELExplicit level: disabled, basic, or detailed. Overrides the level set by MOTUS_TRACING but not its export behavior.basic
MOTUS_TRACING_EXPORT1 enables file export only (without changing the collection level)off
MOTUS_TRACING_ONLINE1 enables detailed collection + file export + live SSE vieweroff
MOTUS_TRACING_DIRCustom output directorytraces/trace_<timestamp>/

Export formats

After a run, TraceManager.export_trace() writes the following files to the output directory:
FileDescription
tracer_state.jsonRaw span metadata — timing, parent relationships, and extracted fields
trace_viewer.htmlInteractive span tree with timing bars and search. Opens automatically on exit when MOTUS_TRACING=1.
jaeger_traces.jsonOpenTelemetry-format spans for Jaeger, Zipkin, or any OTLP backend

Lifecycle hooks

Tracing is built on HookManager. You can register callbacks at three levels of specificity global (every task), per-name (a specific function or tool), and per-type (all tool calls or all model calls).

Registering hooks

from motus.runtime.hooks import (
    register_hook,        # global — fires for every task
    register_task_hook,   # per-name — fires for a specific function or tool
    register_tool_hook,   # per-type — fires for all tool calls
    register_model_hook,  # per-type — fires for all model calls
)

register_hook("task_end", my_callback)
register_task_hook("web_search", "task_end", my_callback)
register_tool_hook("task_end", my_callback)

Decorator equivalents

from motus.runtime.hooks import global_hook, task_hook, tool_task_hook

@global_hook("task_error")
def on_error(event):
    logging.error(f"{event.name} failed: {event.error}")

@task_hook("fetch_data", "task_end")
def on_fetch(event):
    logging.info(f"Fetched: {event.result}")

@tool_task_hook("task_end")
def on_tool(event):
    logging.info(f"Tool {event.name}: {event.result}")
Execution order within each event: global hooks, then name hooks, then type hooks. Pass prepend=True to run a callback first within its group. Both sync and async callbacks are supported. Exceptions in callbacks are logged and never propagated.

HookEvent fields

FieldTypeDescription
event_typestr"task_start", "task_end", "task_error", or "task_cancelled"
namestrFunction or tool name
task_typestr"normal_task", "tool_call", "model_call", "agent_call", or "magic_task"
argstuplePositional arguments passed to the task
kwargsdictKeyword arguments passed to the task
resultAnyReturn value (populated on task_end only)
errorExceptionException instance (populated on task_error and task_cancelled only)
task_idAgentTaskIdUnique identifier for this task instance
metadatadictAdditional context, such as parent_stack

Programmatic access

from motus.runtime import get_runtime

tracer = get_runtime().scheduler.tracer  # auto-inits runtime if needed

tracer.export_trace()
tracer.get_trace_id()           # UUID for this session

for task_id, meta in tracer.task_meta.items():
    print(f"{meta['func']}: {meta.get('ended_at', 'running')}")

Advanced configuration

For fine-grained control, construct a TraceConfig directly instead of relying on environment variables:
from pathlib import Path
from motus.runtime.tracing import TraceConfig, CollectionLevel

config = TraceConfig(
    collection_level=CollectionLevel.DETAILED,
    export_enabled=True,
    log_dir=Path("my_traces/run_001"),
)
FieldTypeDefaultDescription
collection_levelCollectionLevelfrom envWhat data to collect
export_enabledboolfrom envWrite trace files on export
online_tracingboolfrom envStart a local SSE viewer
log_dirPathtraces/trace_<ts>/Output directory
json_pathstr"tracer_state.json"JSON state filename
cloud_api_url`strNone`from credentialsCloud API endpoint
cloud_api_key`strNone`from credentialsCloud API key
project`strNone`MOTUS_PROJECT / motus.tomlProject tag for cloud traces
build`strNone`MOTUS_BUILD / motus.tomlBuild tag for cloud traces

Self-managed Agent tracing

With a Motus Cloud account, spans stream to the Motus dashboard in real time. Authenticate with:
motus login
Then run your agent with tracing enabled:
MOTUS_TRACING=1 python my_agent.py
Tag traces with project and build identifiers for easier filtering in the dashboard:
MOTUS_PROJECT=my-project MOTUS_BUILD=v1.2.3 python my_agent.py

Cloud Tracing