Skip to content

[Bug]: No programmatic API to set a base endpoint with automatic signal path appending for HTTP exporters #3427

@aswiniverse

Description

@aswiniverse

Problem

The Rust OTLP HTTP exporters have no programmatic way to provide a base URL and have the SDK automatically append the signal path (/v1/logs, /v1/traces, /v1/metrics).

.with_endpoint() treats its argument as a full URL and uses it verbatim — there is no programmatic equivalent of OTEL_EXPORTER_OTLP_ENDPOINT (which does append the signal path).

Comparison with Go SDK

The Go SDK distinguishes between two use cases via two separate methods:

// Base URL → SDK appends /v1/logs automatically ✅
otlploghttp.WithEndpoint("localhost:4318")

// Full URL → used as-is ✅
otlploghttp.WithEndpointURL("http://localhost:4318/v1/logs")

The Rust SDK only has one method:

// Always treated as full URL → signal path never appended ✗
.with_endpoint("http://localhost:4318")

There is no Rust equivalent of Go's WithEndpoint(host:port).

Root cause in exporter/http/mod.rs

resolve_http_endpoint (L705–735) has three code paths:

// 1. Programmatic .with_endpoint() → returned as-is, no path appended ✗
if let Some(provider_endpoint) = provided_endpoint.filter(|s| !s.is_empty()) {
    return provider_endpoint.parse()
}

// 2. Signal-specific env var (e.g. OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) → returned as-is ✗
// (comment: "per signal env var is not modified")

// 3. Generic OTEL_EXPORTER_OTLP_ENDPOINT → /v1/logs appended ✓
build_endpoint_uri(&s, signal_endpoint_path)

Signal path appending is only reachable via the generic env var. There is no programmatic equivalent.

Impact

Applications that configure the OTLP endpoint programmatically (e.g. reading from a config file) cannot provide a base URL — they must either:

  • Hardcode the full signal path (e.g. http://localhost:4318/v1/logs), or
  • Fall back to setting OTEL_EXPORTER_OTLP_ENDPOINT as an environment variable at runtime

This is a real footgun for library consumers such as Codex CLI, which reads an endpoint from user config and calls .with_endpoint() — requiring users to manually include /v1/logs in their config.

Suggested fix

Add a with_base_endpoint() method (or a flag on with_endpoint()) that appends the signal path, mirroring Go's WithEndpoint(host:port):

// New API — behaves like OTEL_EXPORTER_OTLP_ENDPOINT
.with_base_endpoint("http://localhost:4318")
// → exports to http://localhost:4318/v1/logs

Workaround

# Unset programmatic endpoint and use env var instead
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# SDK appends /v1/logs, /v1/traces, /v1/metrics automatically

Environment

  • Crate: opentelemetry-otlp
  • Transport: HTTP
  • Signals affected: Logs, Traces, Metrics

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions