Skip to main content
The @pandaprobe.trace and @pandaprobe.span decorators provide automatic instrumentation for your functions. They handle timing, error capture, and input/output extraction with minimal boilerplate.

@pandaprobe.trace

Creates a new trace for the decorated function. Use this on your top-level entry points. Can be used with or without parentheses:
Input/output capture
  • Input: Automatically captured from function arguments (uses inspect.signature to build a JSON-friendly dict).
  • Output: Automatically captured from the return value.
  • At the trace level, the SDK extracts only the last user message from input and the last assistant message from output.
Trace views often focus on the conversational turn. Full structured arguments remain available in raw span data when you need deeper inspection.

@pandaprobe.span

Creates a new span within the current trace. Use this on inner functions.
@pandaprobe.span requires an active trace context. If no trace exists (no enclosing @pandaprobe.trace or pandaprobe.start_trace()), the function runs without instrumentation.

Sync and async support

Both decorators auto-detect sync vs async functions and wrap accordingly.

Nesting

This produces: Trace("support-agent")Span("retrieve", RETRIEVER)Span("generate", LLM).

Combining with wrappers

The wrapper’s LLM span is automatically nested as a child of the trace.
Pair decorators with provider wrappers so LLM calls inherit hierarchy and you still get token usage and model metadata on child spans.

No-op behavior

If the SDK is disabled (PANDAPROBE_ENABLED=false) or no client is available, both decorators pass through transparently: the decorated function runs normally with no overhead.
No-op mode is intentional for local development and tests where you omit API keys or disable tracing globally.