Applies to:
- Plan -
- Deployment -
Summary
Braintrust-hosted organizations can send the OTel logs signal directly to/otel/v1/logs, which converts each log record into a span. Self-hosted deployments accept the traces signal only, so a source that emits log records needs a translation step until the logs endpoint reaches a data plane release.
What is happening
The OTLP intake exposes a signal-specific endpoint for each signal it accepts. Which endpoints your organization has depends on where its data plane runs:
For Collector or SDK configs that expect the base OTLP endpoint rather than a signal-specific one, use
/otel and let the exporter append the signal path.
The logs endpoint is not in a released data plane version, so it is not available on self-hosted or custom data plane deployments. To confirm which endpoint your organization uses, check its API URL at Settings > Data plane. See Data plane region.
Fix or suggestion
Braintrust-hosted: send OTLP logs directly
Point the logs pipeline at your organization’s API URL. The endpoint acceptsapplication/json and application/x-protobuf, and it authenticates with the same headers as the traces endpoint.
https://api-eu.braintrust.dev/otel for EU-hosted organizations.
Each log record becomes a span. A record that carries a trace ID joins that trace, parented under its span ID when the record has one. A record without a trace ID becomes its own root span. Braintrust maps GenAI semantic-convention attributes onto span fields, and records the record’s body along with its severity number and severity text.
The .NET SDK sends this signal for you. Its BRAINTRUST_LOGS_PATH variable defaults to /otel/v1/logs, so no configuration is needed on a hosted organization. See the C# API reference.
Self-hosted: send spans instead of log records
Because the logs endpoint is not available on a self-hosted deployment, translate log records into spans and send those. If your adapter can emit real OTLP spans, forward them to the traces endpoint. This is the least work to maintain:- Read OTLP log records from the source.
- Group related records by a stable ID, such as
session.idorprompt.id. - Create one synthetic root span per group.
- Convert each log record into a child span.
- Insert the translated spans with
POST /v1/project_logs/{project_id}/insert.
Minimal insert example:
transform processor can modify log attributes, but it does not by itself promote log records into trace spans. For production log-to-span translation, use a small adapter service or a custom Collector connector that creates real spans.
How to confirm it worked
- Open Logs and confirm spans appear for the records you sent.
- If you send OTLP logs directly, confirm that records carrying a trace ID appear inside that trace rather than as separate root spans.
- If you translate records into spans, confirm one trace appears per grouped
session.idorprompt.id, and that the synthetic root span contains session metadata. - Confirm token and cost fields appear under
metrics. - If the response reports rejected log records, check that each record has a resolvable parent. A
403means the API key cannot write to the project named inx-bt-parent.