opentelemetry
Python SDK configuration
Install the Braintrust Python SDK with OpenTelemetry support:
pip install "braintrust[otel]"
Configure these environment variables:
BRAINTRUST_API_KEY=your-api-key
BRAINTRUST_PARENT=project_name:my-otel-project
# BRAINTRUST_API_URL=https://api.braintrust.dev
# - US data plane: Optional (defaults to https://api.braintrust.dev)
# - EU data plane: https://api-eu.braintrust.dev
# - Self-hosted data plane: Your data plane URL
For Python applications, use the BraintrustSpanProcessor for simplified configuration:
import os
from braintrust.otel import BraintrustSpanProcessor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
# Configure the global OTel tracer provider
provider = TracerProvider()
trace.set_tracer_provider(provider)
# Send spans to Braintrust.
provider.add_span_processor(BraintrustSpanProcessor())
For more advanced configuration, you can pass in the following arguments to BraintrustSpanProcessor:
api_key: The API key to use for Braintrust. Defaults to theBRAINTRUST_API_KEYenvironment variable.api_url: The URL of the Braintrust API. Defaults to theBRAINTRUST_API_URLenvironment variable orhttps://api.braintrust.devif not set.parent: The parent project or experiment to use for Braintrust. Defaults to theBRAINTRUST_PARENTenvironment variable.filter_ai_spans: Defaults toFalse. IfTrue, only AI-related spans will be sent to Braintrust.custom_filter: A function that gives you fine-grained control over which spans are sent to Braintrust.
TypeScript SDK configuration
Starting with v1.0, OpenTelemetry functionality has been moved to the separate @braintrust/otel npm package. This solves ESM build issues in Next.js (edge), Cloudflare Workers, Bun, and TanStack applications, and adds support for both OpenTelemetry v1 and v2.
Install the Braintrust TypeScript SDK with the following OpenTelemetry dependencies:
# pnpm
pnpm add braintrust @braintrust/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-base
# npm
npm install braintrust @braintrust/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-base
For TypeScript applications, use the BraintrustSpanProcessor with NodeSDK:
import { NodeSDK } from "@opentelemetry/sdk-node";
import { BraintrustSpanProcessor } from "@braintrust/otel";
const sdk = new NodeSDK({
serviceName: "my-service",
spanProcessor: new BraintrustSpanProcessor({
parent: "project_name:your-project-name",
}),
});
sdk.start();
For more advanced configuration, you can pass in the following arguments to BraintrustSpanProcessor:
apiKey: The API key to use for Braintrust. Defaults to theBRAINTRUST_API_KEYenvironment variable.apiUrl: The URL of the Braintrust API. Defaults to theBRAINTRUST_API_URLenvironment variable orhttps://api.braintrust.devif not set.parent: The parent project or experiment to use for Braintrust. Defaults to theBRAINTRUST_PARENTenvironment variable.filterAISpans: Defaults tofalse. Iftrue, only AI-related spans will be sent to Braintrust.customFilter: A function that gives you fine-grained control over which spans are sent to Braintrust.
OTel compatibility
Braintrust interoperates with OpenTelemetry at two levels: an OTel-compatible ID and export format, and compatibility mode.
ID and export format
Braintrust can represent span and trace IDs as OTel-compatible hexadecimal values and serialize them in a shared export format, so exported spans interoperate with OpenTelemetry. Support for the OTel-compatible format, including reading spans propagated through the x-bt-parent header or other distributed-tracing channels, differs by SDK:
- Python SDK: reads either format (auto-detecting) as of v0.3.0, and exports the OTel-compatible format by default as of v0.26.0. To export Braintrust’s legacy UUID-style format instead, set
BRAINTRUST_LEGACY_IDS=true. - TypeScript SDK: reads and exports only the legacy format by default. Enable compatibility mode with
@braintrust/otel’ssetupOtelCompat().
Compatibility mode
In addition to using the compatible format, compatibility mode stores the active span in OpenTelemetry’s context, so Braintrust and OpenTelemetry instrumentation contribute to the same trace within a process.
To enable compatibility mode in Python, set an environment variable; in TypeScript, call a setup function. The examples below enable it and send spans to Braintrust.
- Python
import os
# Enable OTel compatibility before imports
os.environ["BRAINTRUST_OTEL_COMPAT"] = "true"
os.environ["BRAINTRUST_API_KEY"] = "<your-api-key>"
from braintrust import Eval
from braintrust.otel import BraintrustSpanProcessor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
# Set up OTel tracing
provider = TracerProvider()
provider.add_span_processor(BraintrustSpanProcessor(parent="project_name:my-project"))
trace.set_tracer_provider(provider)
- TypeScript
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
import { trace } from "@opentelemetry/api";
import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
import { setupOtelCompat, BraintrustSpanProcessor } from "@braintrust/otel";
// Setup context manager to group span
const contextManager = new AsyncLocalStorageContextManager();
contextManager.enable();
context.setGlobalContextManager(contextManager);
const braintrustProcessor = new BraintrustSpanProcessor({
parent: "project_name:my-braintrust-project",
filterAISpans: true,
});
const provider = new BasicTracerProvider({
spanProcessors: [braintrustProcessor]
});
trace.setGlobalTracerProvider(provider);
// Call this first, before any logger or span creation
setupOtelCompat();
Distributed tracing
You can do distributed tracing between services instrumented with the Braintrust SDK and OpenTelemetry, either to create OpenTelemetry spans as children of Braintrust spans or to create Braintrust spans as children of OpenTelemetry spans. The examples below stitch the OpenTelemetry SDK and Braintrust SDK together using the @braintrust/otel package.
Create OpenTelemetry spans as children of Braintrust spans
Export the Braintrust span context and use it to create an OpenTelemetry context.
import braintrust
from braintrust.otel import context_from_span_export
from opentelemetry import context as otel_context
from opentelemetry import trace
# Service A: Create Braintrust span and export context
project = braintrust.init_logger(project="my-project")
with project.start_span(name="service_a") as span:
exported = span.export()
# Send to Service B via HTTP
import requests
requests.post("https://service-b/api", headers={"x-braintrust-context": exported})
# Service B: Receive request and create OTel span as child
exported = request.headers.get("x-braintrust-context")
cctx = context_from_span_export(exported)
token = otel_context.attach(ctx)
try:
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("service_b") as span:
# This span is now a child of the Braintrust span
pass
finally:
otel_context.detach(token)
OTLP configuration
If you are using a different language or want to use pure OTel code, you can set up the OpenTelemetry Protocol Exporter (OTLP) to send traces to Braintrust. Once you set up an OTLP exporter to send traces to Braintrust, we automatically convert LLM calls into Braintrust LLM spans, which can be saved as prompts and evaluated in the playground.
For collectors that use the OpenTelemetry SDK to export traces, set the following environment variables:
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.braintrust.dev/otel
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <Your API Key>, x-bt-parent=project_id:<Your Project ID>"
The trace endpoint URL is https://api.braintrust.dev/otel/v1/traces. If your organization is on the EU data plane, use https://api-eu.braintrust.dev/otel instead. If you’re self-hosting Braintrust, substitute your stack’s Universal API URL.
Manual tracing
If you want to log LLM calls directly to the OTel endpoint, you can set up a custom OpenTelemetry tracer and add the appropriate attributes to your spans. This gives you fine-grained control over what data gets logged.
GenAI attributes
Braintrust implements the OpenTelemetry GenAI semantic conventions. When you send traces with these attributes, they are automatically mapped to Braintrust fields.