@observe decorator handles span creation automatically. But sometimes you need direct control:
- Conditional tracing — Start a span only if certain conditions are met.
- Dynamic naming — Determine the span name based on runtime data.
- Long-running operations — Hold a span open across multiple function calls.
- Non-function boundaries — Trace a block of code that isn’t a function.
Choosing the Right Method
It’s important to distinguish:- Creating a span (starts timing) vs activating a span (makes it the current parent for nesting)
- Active spans (recommended) vs detached/manual spans (advanced)
- Active span: create a folder and open it — new files go inside automatically.
- Detached span: create a folder but don’t open it — new files won’t go inside unless you explicitly place them there.
Span Lifecycle
A manual span follows three steps:- Start — Create the span with a name. It becomes the “current” span, and any child spans nest under it.
- Enrich — Add attributes, set input/output, record events or errors.
- End — Close the span. This records the end time and sends the span to Laminar.
with in Python) or try/finally blocks to ensure spans always close, even when errors occur.
Common Options (and When to Use Them)
- TypeScript
- Python
In the Laminar UI
- Manually-created spans look the same as auto-instrumented spans: a node in the trace tree with timing and attributes.
- If a span is active, any work executed inside it is parented underneath it automatically.
Span Types
Spans can have a type that affects how they appear in the Laminar UI:- DEFAULT — General-purpose span.
- LLM — Language model call (enables token counts, cost tracking).
- TOOL — Tool or function call within an agent.
- EXECUTOR — Orchestration or routing logic.
