## Python SDK configuration

Install the Braintrust Python SDK with OpenTelemetry support:

```bash
pip install "braintrust[otel]"
```

Configure these environment variables:

```.env
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:

```python
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 the `BRAINTRUST_API_KEY` environment variable.
- `api_url`: The URL of the Braintrust API. Defaults to the `BRAINTRUST_API_URL` environment variable or `https://api.braintrust.dev` if not set.
- `parent`: The parent project or experiment to use for Braintrust. Defaults to the `BRAINTRUST_PARENT` environment variable.
- `filter_ai_spans`: Defaults to `False`. If `True`, 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](/content/docs/sdks/typescript/quickstart/index.html) with the following OpenTelemetry dependencies:

```bash
# 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:

```typescript
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 the `BRAINTRUST_API_KEY` environment variable.
- `apiUrl`: The URL of the Braintrust API. Defaults to the `BRAINTRUST_API_URL` environment variable or `https://api.braintrust.dev` if not set.
- `parent`: The parent project or experiment to use for Braintrust. Defaults to the `BRAINTRUST_PARENT` environment variable.
- `filterAISpans`: Defaults to `false`. If `true`, 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`’s `setupOtelCompat()`.

### 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

```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

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

```python
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](https://opentelemetry.io/docs/languages/js/exporters/) to send traces to Braintrust, we automatically convert LLM calls into Braintrust `LLM` spans, which can be saved as [prompts](/content/docs/deploy/prompts/index.html) and evaluated in the [playground](/content/docs/evaluate/playgrounds/index.html).

For collectors that use the [OpenTelemetry SDK](https://opentelemetry.io/docs/languages/) 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](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md). When you send traces with these attributes, they are automatically mapped to Braintrust fields.
