ommx.tracing#
Per-cell / per-block tracing for OMMX.
Two user-facing APIs, sharing the same OTel collector + renderers:
Jupyter cell magic (best for notebooks):
%load_ext ommx.tracing
%%ommx_trace
instance = Instance.from_bytes(blob)
solution = instance.evaluate(state)
Cell output shows a nested text tree of every span produced during the cell (Rust and Python alike), annotated with durations and attributes, plus a download link for the full trace in Chrome Trace Event Format.
Context manager / decorator (best for scripts, tests, CI):
from ommx.tracing import capture_trace, render_text_tree, save_chrome_trace, traced
with capture_trace() as trace:
solution = instance.evaluate(state)
print(render_text_tree(trace))
save_chrome_trace(trace, "trace.json")
@traced(output="process.json")
def process():
...
The public surface is intentionally small:
capture_trace— context manager;__enter__returns aTraceResultplaceholder that__exit__fills in (for success and for exceptions — information is never dropped).TraceResult— completed trace data:request,spans,otlp_protobuf().render_text_tree(),chrome_trace_json(),save_chrome_trace()— render or save aTraceResult.traced()— decorator sugar on top ofcapture_trace, optionally writing the Chrome Trace JSON to disk.load_ipython_extension()— wired by%load_ext ommx.tracing; registers the%%ommx_tracecell magic. The collector itself is attached to the active OpenTelemetryTracerProviderlazily, on the first traced cell, so%load_extstays cheap and cannot fail due to provider state that is still being configured further up the notebook.unload_ipython_extension()— pair for%unload_ext. Kept as a no-op because IPython magics cannot be cleanly unregistered without disturbing user state; leaving the collector installed costs nothing.
Everything else (_collector, _render, _setup, _magic,
_capture, _decorator, _result) is internal and may change
without notice. Reach for them only if you are building on top of this
module and can tolerate breakage.
Classes#
Populated result of a |
|
Context manager that captures every OTel span inside the block. |
Functions#
|
|
|
Register the |
|
Render |
|
Write |
|
Decorator that runs the wrapped function under |
|
IPython extension hook, kept as a no-op. |
Package Contents#
- class TraceResult#
Populated result of a
capture_traceblock.Filled in by
capture_traceon__exit__(including the exception path, so the caller can always inspect the trace even when the block raised).- classmethod from_otlp_protobuf(payload: bytes) TraceResult#
Build a trace result from an OMMX trace payload.
- request: opentelemetry.proto.collector.trace.v1.trace_service_pb2.ExportTraceServiceRequest#
- class capture_trace(name: str = _DEFAULT_ROOT_SPAN_NAME, tracer_name: str = _TRACER_NAME)#
Context manager that captures every OTel span inside the block.
The root span is started with an explicit empty OTel
Contextso each block gets its own freshtrace_idregardless of any ambient spans — the collector keys captures bytrace_id, so without this guard sibling spans from unrelated instrumentation would bleed into the result.
- load_ipython_extension(ipython: IPython.core.interactiveshell.InteractiveShell) None#
Register the
%%ommx_tracecell magic onipython.Invoked by IPython when the user runs
%load_ext ommx.tracing. Safe to call more than once — later calls are no-ops since IPython dedupes magics by name.
- render_text_tree(result: ommx.tracing._result.TraceResult) str#
Render
resultas a nested ASCII tree, one root per top-level span.The tree preserves parent→child relationships as recorded by OTel. Siblings are sorted by start time so the output reflects execution order.
- save_chrome_trace(result: ommx.tracing._result.TraceResult, path: str | Path) None#
Write
resultas Chrome Trace JSON topath.Overwrites any existing file. The UTF-8 encoding matches the JSON spec and is what Perfetto / speedscope /
chrome://tracingall accept.
- traced(func: _F) _F#
- traced(*, name: str | None = ..., output: str | Path | None = ...) Callable[[_F], _F]
Decorator that runs the wrapped function under
capture_trace.Supports all three call shapes:
@traced def process(): ... @traced() def process(): ... @traced(name="build_qubo", output="qubo.json") def process(): ...
If
outputis given, the Chrome Trace JSON is written to that path when the function returns or raises — information is never dropped. The exception, if any, is re-raised unchanged after the file is written.If
nameis omitted, the span is named after the function (fn.__qualname__) so traces from multiple decorated functions are easy to tell apart in the rendered tree.
- unload_ipython_extension(ipython: IPython.core.interactiveshell.InteractiveShell) None#
IPython extension hook, kept as a no-op.
Removing a previously-registered magic leaves the shell in an awkward state (the name still resolves for tab completion), and there is no user-observable state to tear down — the collector sheds its entries on retrieval already.