Skip to main content

Basic Tracing

ABV provides flexible ways to create and manage traces and their constituent observations (spans and generations).

Install package

@observe Decorator

The @observe() decorator provides a convenient way to automatically trace function executions, including capturing their inputs, outputs, execution time, and any errors. It supports both synchronous and asynchronous functions.

Parameters:

  • name: Optional[str]: Custom name for the created span or generation observation. Defaults to the function name.
  • as_type: Optional[Literal["generation"]]: If set to "generation", a ABV generation object is created, suitable for LLM calls. Otherwise, a regular span is created.
  • capture_input: bool: Whether to capture function arguments as input. Defaults to env var ABV_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED or True if not set.
  • capture_output: bool: Whether to capture function return value as output. Defaults to env var ABV_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED or True if not set.
  • transform_to_string: Optional[Callable[[Iterable], str]]: For functions that return generators (sync or async), this callable can be provided to transform the collected chunks into a single string for the output field. If not provided, and all chunks are strings, they will be concatenated. Otherwise, the list of chunks is stored.

Trace Context and Special Keyword Arguments:

The @observe decorator automatically propagates the OTEL trace context. If a decorated function is called from within an active ABV span (or another OTEL span), the new observation will be nested correctly. You can also pass special keyword arguments to a decorated function to control its tracing behavior:
  • abv_trace_id: str: Explicitly set the trace ID for this function call. Must be a valid W3C Trace Context trace ID (32-char hex). If you have a trace ID from an external system, you can use ABV.create_trace_id(seed=external_trace_id) to generate a valid deterministic ID.
  • abv_parent_observation_id: str: Explicitly set the parent observation ID. Must be a valid W3C Trace Context span ID (16-char hex).
The observe decorator is capturing the args, kwargs and return value of decorated functions by default. This may lead to performance issues in your application if you have large or deeply nested objects there. To avoid this, explicitly disable function IO capture on the decorated function by passing capture_input=False and/or capture_output=False parameters.

Context Managers

You can create spans or generations anywhere in your application. If you need more control than the @observe decorator, the primary way to do this is using context managers (with with statements), which ensure that observations are properly started and ended.
  • abv.start_as_current_span(): Creates a new span and sets it as the currently active observation in the OTEL context for its duration. Any new observations created within this block will be its children.
  • abv.start_as_current_generation(): Similar to the above, but creates a specialized โ€œgenerationโ€ observation for LLM calls.

Manual Observations

For scenarios where you need to create an observation (a span or generation) without altering the currently active OpenTelemetry context, you can use abv.start_span() or abv.start_generation().
If you use manual span management with start_span() or start_generation(), you must remember to call .end() on each observation to ensure data is properly sent to ABV. Failing to end observations can lead to incomplete traces.

Key Characteristics:

  • No Context Shift: Unlike their start_as_current_... counterparts, these methods do not set the new observation as the active one in the OpenTelemetry context. The previously active span (if any) remains the current context for subsequent operations in the main execution flow.
  • Parenting: The observation created by start_span() or start_generation() will still be a child of the span that was active in the context at the moment of its creation.
  • Manual Lifecycle: These observations are not managed by a with block and therefore must be explicitly ended by calling their .end() method.
  • Nesting Children:
    • Subsequent observations created using the global abv.start_as_current_span() (or similar global methods) will not be children of these โ€œmanualโ€ observations. Instead, they will be parented by the original active span.
    • To create children directly under a โ€œmanualโ€ observation, you would use methods on that specific observation object (e.g., manual_span.start_as_current_span(...)).
When to Use: This approach is useful when you need to:
  • Record work that is self-contained or happens in parallel to the main execution flow but should still be part of the same overall trace (e.g., a background task initiated by a request).
  • Manage the observationโ€™s lifecycle explicitly, perhaps because its start and end are determined by non-contiguous events.
  • Obtain an observation object reference before itโ€™s tied to a specific context block.
Example with more complex nesting:

Nesting Observations

Observe Decorator

The function call hierarchy is automatically captured by the @observe decorator reflected in the trace.

Context Managers

Nesting is handled automatically by OpenTelemetryโ€™s context propagation. When you create a new observation (span or generation) using start_as_current_span or start_as_current_generation, it becomes a child of the observation that was active in the context when it was created.

Manual

If you are creating observations manually (not _as_current_), you can use the methods on the parent ABVSpan or ABVGeneration object to create children. These children will not become the current context unless their _as_current_ variants are used.

Updating Observations

You can update observations with new information as your code executes.
  • For spans/generations created via context managers or assigned to variables: use the .update() method on the object.
  • To update the currently active observation in the context (without needing a direct reference to it): use abv.update_current_span() or abv.update_current_generation().
ABVSpan.update()** / ABVGeneration.update() parameters:**

Setting Trace Attributes

Trace-level attributes apply to the entire trace, not just a single observation. You can set or update these using:
  • The .update_trace() method on any ABVSpan or ABVGeneration object within that trace.
  • abv.update_current_trace() to update the trace associated with the currently active observation.
Trace attribute parameters: Example: Setting Multiple Trace Attributes

Trace Input/Output Behavior

Trace input and output are automatically set from the root observation (first span/generation) by default.

Default Behavior

Override Default Behavior

If you need different trace inputs/outputs than the root observation, explicitly set them:

Critical for LLM-as-a-Judge Features

LLM-as-a-judge and evaluation features typically rely on trace-level inputs and outputs. Make sure to set these appropriately:

Trace and Observation IDs

ABV uses W3C Trace Context compliant IDs:
  • Trace IDs: 32-character lowercase hexadecimal string (16 bytes).
  • Observation IDs (Span IDs): 16-character lowercase hexadecimal string (8 bytes).
You can retrieve these IDs:
  • abv.get_current_trace_id(): Gets the trace ID of the currently active observation.
  • abv.``get_current_observation_id``(): Gets the ID of the currently active observation.
  • span_obj.trace_id and span_obj.id: Access IDs directly from a ABVSpan or ABVGeneration object.
For scenarios where you need to generate IDs outside of an active trace (e.g., to link scores to traces/observations that will be created later, or to correlate with external systems), use:
  • ABV.create_trace_id(seed: Optional[str] = None)(static method): Generates a new trace ID. If a seed is provided, the ID is deterministic. Use the same seed to get the same ID. This is useful for correlating external IDs with ABV traces.
Linking to Existing Traces (Trace Context) If you have a trace_id (and optionally a parent_span_id) from an external source (e.g., another service, a batch job), you can link new observations to it using the trace_context parameter. Note that OpenTelemetry offers native cross-service context propagation, so this is not necessarily required for calls between services that are instrumented with OTEL.

Client Management

flush()

Manually triggers the sending of all buffered observations (spans, generations, scores, media metadata) to the ABV API. This is useful in short-lived scripts or before exiting an application to ensure all data is persisted.
The flush() method blocks until the queued data is processed by the respective background threads.

shutdown()

Gracefully shuts down the ABV client. This includes:
  1. Flushing all buffered data (similar to flush()).
  2. Waiting for background threads (for data ingestion and media uploads) to finish their current tasks and terminate.
Itโ€™s crucial to call shutdown() before your application exits to prevent data loss and ensure clean resource release. The SDK automatically registers an atexit hook to call shutdown() on normal program termination, but manual invocation is recommended in scenarios like:
  • Long-running daemons or services when they receive a shutdown signal.
  • Applications where atexit might not reliably trigger (e.g., certain serverless environments or forceful terminations).

Integrations

Third-party integrations

The ABV SDK seamlessly integrates with any third-party library that uses OpenTelemetry instrumentation. When these libraries emit spans, they are automatically captured and properly nested within your trace hierarchy. This enables unified tracing across your entire application stack without requiring any additional configuration. For example, if youโ€™re using OpenTelemetry-instrumented databases, HTTP clients, or other services alongside your LLM operations, all these spans will be correctly organized within your traces in ABV.

Example Anthropic

You can use any third-party, OTEL-based instrumentation library for Anthropic to automatically trace all your Anthropic API calls in ABV. In this example, we are using the opentelemetry-instrumentation-anthropic. install packages

Example LlamaIndex

You can use the third-party, OTEL-based instrumentation library for LlamaIndex to automatically trace your LlamaIndex calls in ABV. In this example, we are using the openinference-instrumentation-llama-index. install packages