Skip to main content

Observability Core Concepts

Basalt’s observability system is built on OpenTelemetry, providing deep insights into your LLM application’s behavior through distributed tracing, automatic instrumentation, and intelligent evaluation attachment.

What is Observability?

Observability in Basalt allows you to:
  • Trace execution flows from prompt retrieval through LLM calls to final outputs
  • Monitor performance with automatic timing and token usage tracking
  • Evaluate quality by attaching evaluators to specific operations
  • Track identity by associating user and organization context with operations
  • Debug issues with detailed span hierarchies and error tracking

OpenTelemetry Architecture

Traces and Spans

Basalt uses OpenTelemetry’s trace and span model to represent your application’s execution:
Key Concepts:
  • Trace: A complete journey through your system, identified by a unique trace ID. All related operations share this ID.
  • Span: A single operation within a trace, representing a unit of work (function call, API request, database query).
  • Root Span: The entry point of a trace, created with start_observe. Every trace must have exactly one root span.
  • Child Spans: Nested operations within a parent span, created with observe.

Span Hierarchy

Spans form a parent-child tree structure:
This creates:

Context Propagation

One of Basalt’s most powerful features is automatic context propagation. When you set identity, evaluators, or metadata on a parent span, they automatically flow to all child spans.

How Context Propagation Works

Basalt uses OpenTelemetry’s context mechanism to propagate data:
  1. Context Storage: Data is stored in thread-local (or async-local) context
  2. Automatic Inheritance: Child spans read from parent context
  3. Span Processors: The BasaltContextProcessor applies context to spans on creation

What Gets Propagated

Identity (User & Organization):
Evaluators:
Metadata:
Experiments:

Span Kinds

Basalt defines semantic span kinds to categorize operations:
Span kinds enable:
  • Semantic filtering in dashboards
  • Kind-specific evaluators
  • Performance analysis by operation type

Evaluator Attachment

Evaluators are quality checks that run on span data after execution. Understanding how evaluators attach to spans is crucial for effective observability.

Attachment Flow

Two Attachment Modes

Propagating (affects children):
  • @evaluator(slugs=[...]) decorator
  • with_evaluators(...) context manager
  • attach_evaluator(...) context manager
  • Global: configure_trace_defaults(evaluators=[...])
Non-propagating (span-only):
  • span.add_evaluator(slug) method
  • attach_evaluators_to_span(...) helper

Sampling

Control evaluation costs with sampling:

Prompt Integration

When you fetch a prompt using the context manager pattern, Basalt automatically creates a prompt span and injects attributes into subsequent LLM calls.

Automatic Attribute Injection

The Complete Flow

This automatic linking enables:
  • Tracking which prompt version was used for each generation
  • A/B testing prompt variations
  • Debugging prompt-related issues
  • Analyzing performance by prompt

Identity Tracking

Identity tracking associates user and organization context with traces, enabling per-user analytics and debugging.

Structure

Setting Identity

At root span:
Dynamically:
From function arguments (callable pattern):

Benefits

  • Filter traces by user or organization
  • Debug user-specific issues
  • Track usage per customer
  • Implement user-based rate limiting
  • Generate per-user analytics

Experiments

Experiments enable A/B testing, model comparison, and variant tracking.
All spans in each variant are tagged with the experiment ID, enabling:
  • Compare metrics between variants
  • Track experiment performance over time
  • Evaluate variant quality differences

Trace boundaries and experiments in loops

Trace boundaries are determined by start_observe() context scoping, not by the Basalt client instance. Each start_observe() call creates a new root span — and when no parent span is active, OpenTelemetry assigns a fresh trace_id. This means you can process multiple items under one experiment in a loop without recreating the client:
Create one Basalt instance per process. shutdown() permanently destroys the global TracerProvider — call it only at process exit. See Experiments examples for a full batch-evaluation pattern.

Auto-Instrumentation

Basalt automatically instruments popular LLM providers, vector databases, and frameworks without code changes.

How It Works

Auto-instrumented spans:
  • Inherit evaluators from parent context
  • Inherit identity (user/org) from parent context
  • Automatically capture provider-specific attributes (model, tokens, etc.)
  • Work seamlessly with manual @observe decorators
Supported providers include 10 LLM providers, 3 vector databases, and 3 frameworks (see Auto-Instrumentation guide for full list).

Summary

Basalt’s observability system provides:
  1. OpenTelemetry-based tracing - Industry-standard distributed tracing
  2. Automatic context propagation - Identity, evaluators, and metadata flow to children
  3. Flexible attachment modes - Propagating and non-propagating evaluators
  4. Prompt integration - Automatic attribute injection for LLM calls
  5. Semantic span kinds - Categorize operations for better analysis
  6. Auto-instrumentation - Zero-code tracing for popular providers
  7. Identity tracking - Per-user and per-org analytics
  8. Experiments - Built-in A/B testing support
Next, explore: