FAQ & Troubleshooting

On this page

Why don't I see any spans?

Check, in order:

  1. Is an exporter registered? Tracer.enabled is False until at least one exporter is registered — call is_tracing_enabled() to check. Registering via any enable_*() helper or get_tracer().register_exporter(...) also turns tracing on.
  2. Did you call disable_tracing() and forget to re-enable? Exporters stay registered across a disable()/enable() cycle — call get_tracer().enable() (not re-registration) to resume.
  3. Did the process exit before spans were flushed? Export happens on a background thread; call get_tracer().flush() before your process exits if you need a guarantee that every queued span has been handed to your exporters. Tracer.shutdown() (registered with atexit) normally does this automatically, but abrupt process termination (e.g. os._exit(), a killed container) can skip atexit handlers entirely.
  4. Is the exporter itself failing silently? Exporter exceptions are caught and logged via Python's logging module (logger.exception(...) in Tracer._drain_loop), not raised into your code — check your logs at the zerda.observability.tracing logger for "Exporter %s failed to export span %s".

enable_langsmith() raises ImportError

The langsmith package isn't installed. It's an optional dependency — install it with pip install langsmith. See Installation → Optional dependency: LangSmith.

My spans aren't nested the way I expected

Nesting (parent_id) is tracked automatically only for spans opened via Tracer.span(), using a contextvars.ContextVar — so a child with get_tracer().span(...) block only becomes a child of the currently open span, which depends on lexical/call nesting and async task boundaries. Two common surprises:

  • Spans reported via record_external_span() are never automatically nested under whatever is "current" — that method doesn't read the ContextVar at all. Pass parent_id and trace_id explicitly if you want a specific relationship.
  • Constructing a bare Span(...) yourself always gets a fresh random trace_id and parent_id=None — grouping only happens through Tracer.span()'s logic of copying the parent's trace_id onto the child.

Does flush(timeout=...) actually time out?

No — per the source, Tracer.flush() calls self._queue.join(), and the timeout parameter is not forwarded anywhere (queue.Queue.join() takes no timeout argument). Passing a timeout value currently has no effect; flush() blocks until the queue is empty regardless. See the warning on Tracer.flush.

Is this OpenTelemetry-compatible out of the box?

No. Span here is this module's own, independent shape — there's no built-in OTel exporter in the provided source. You can write one by subclassing BaseExporter; see the Cookbook for a sketch of what that integration point looks like.

Is it safe to leave tracer.span(...) calls in code paths that never register an exporter?

Yes — that's the explicit design intent stated in the source docstring: until an exporter is registered, span() returns a Span that's never tracked or exported, and constructing it is the only cost. See Architecture → No-op when disabled.

Does this work with asyncio and threads?

Nesting/grouping (parent_id / trace_id) uses a contextvars.ContextVar, which is per-thread and per-asyncio.Task by design — concurrent traces in different tasks/threads don't cross-link with each other. See Architecture → Context propagation. The source does not address multiprocessing; a forked or spawned child process gets its own independent Tracer singleton and queue.

Can I use two separate Tracer instances in the same process?

Nothing prevents constructing Tracer() directly, but doing so gives you an isolated exporter list and queue, disconnected from whatever the rest of the framework calls get_tracer() to reach. Unless you have a specific reason to isolate tracing (e.g. a test harness), use get_tracer() everywhere so all spans share one set of exporters. See Tracer → Constructor.


If this page didn't answer your question, the API Reference documents every method's exact behavior directly from the source, and Architecture explains how the pieces interact.

Related pages