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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions