Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opentelemetry-otlp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## vNext

- Fix `NoHttpClient` error when multiple HTTP client features are enabled by using priority-based selection (`reqwest-client` > `hyper-client` > `reqwest-blocking-client`). [#2994](https://github.com/open-telemetry/opentelemetry-rust/issues/2994)
- Add partial success response handling for OTLP exporters (traces, metrics, logs) per OTLP spec. Exporters now log warnings when the server returns partial success responses with rejected items and error messages. [#865](https://github.com/open-telemetry/opentelemetry-rust/issues/865)
- Refactor `internal-logs` feature in `opentelemetry-otlp` to reduce unnecessary dependencies[3191](https://github.com/open-telemetry/opentelemetry-rust/pull/3192)
- Fixed [#2777](https://github.com/open-telemetry/opentelemetry rust/issues/2777) to properly handle `shutdown_with_timeout()` when using `grpc-tonic`.
Expand Down
30 changes: 14 additions & 16 deletions opentelemetry-otlp/src/exporter/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,22 +212,14 @@ impl HttpExporterBuilder {
#[allow(unused_mut)] // TODO - clippy thinks mut is not needed, but it is
let mut http_client = self.http_config.client.take();

// When multiple HTTP client features are enabled, we use a priority order
// to select the client. This follows Rust's feature unification principle
// where features should be additive. Priority (highest to lowest):
// 1. reqwest-client (async)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is this priority decided? The default we use is the blocking-client (since the detail is the dedicated thread model..). But this order is reverse...

Copy link
Member Author

@scottgerring scottgerring Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking is that when you explicitly turn on another client feature, that feature should win, to match the rust principal that features are additive.

Because blocking is the default feature if it appears first in the fallthrough here users wouldn’t be able to simply toggle on an alternative client feature, they’d have to also turn off default. As Leo points out in the issue this’d be counterintuitive. WDYT?

// 2. hyper-client
// 3. reqwest-blocking-client (default)
if http_client.is_none() {
#[cfg(all(
not(feature = "reqwest-client"),
not(feature = "reqwest-blocking-client"),
feature = "hyper-client"
))]
{
// TODO - support configuring custom connector and executor
http_client = Some(Arc::new(HyperClient::with_default_connector(timeout, None))
as Arc<dyn HttpClient>);
}
#[cfg(all(
not(feature = "hyper-client"),
not(feature = "reqwest-blocking-client"),
feature = "reqwest-client"
))]
#[cfg(feature = "reqwest-client")]
{
http_client = Some(Arc::new(
reqwest::Client::builder()
Expand All @@ -236,9 +228,15 @@ impl HttpExporterBuilder {
.unwrap_or_default(),
) as Arc<dyn HttpClient>);
}
#[cfg(all(not(feature = "reqwest-client"), feature = "hyper-client"))]
{
// TODO - support configuring custom connector and executor
http_client = Some(Arc::new(HyperClient::with_default_connector(timeout, None))
as Arc<dyn HttpClient>);
}
#[cfg(all(
not(feature = "hyper-client"),
not(feature = "reqwest-client"),
not(feature = "hyper-client"),
feature = "reqwest-blocking-client"
))]
{
Expand Down