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:

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:

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:

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.

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)
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.