Skip to content

Commit 19d3223

Browse files
TommyCppcijothomas
andauthored
fix: URLs in OTLP exporters (#1706)
Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com>
1 parent b5fe16e commit 19d3223

File tree

4 files changed

+90
-44
lines changed

4 files changed

+90
-44
lines changed

opentelemetry-otlp/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
### Fixed
66

77
- URL encoded values in `OTEL_EXPORTER_OTLP_HEADERS` are now correctly decoded. [#1578](https://github.com/open-telemetry/opentelemetry-rust/pull/1578)
8+
- OTLP exporter will not change the URL added through `ExportConfig` [#1706](https://github.com/open-telemetry/opentelemetry-rust/pull/1706)
9+
- Default grpc endpoint will not have path based on signal(e.g `/v1/traces`) [#1706](https://github.com/open-telemetry/opentelemetry-rust/pull/1706)
810

911
### Added
1012

opentelemetry-otlp/src/exporter/http/mod.rs

+65-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use super::{default_headers, default_protocol, parse_header_string};
1+
use super::{
2+
default_headers, default_protocol, parse_header_string,
3+
OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
4+
};
25
use crate::{
36
ExportConfig, Protocol, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS,
47
OTEL_EXPORTER_OTLP_TIMEOUT,
@@ -150,7 +153,7 @@ impl HttpExporterBuilder {
150153
signal_timeout_var: &str,
151154
signal_http_headers_var: &str,
152155
) -> Result<OtlpHttpClient, crate::Error> {
153-
let endpoint = resolve_endpoint(
156+
let endpoint = resolve_http_endpoint(
154157
signal_endpoint_var,
155158
signal_endpoint_path,
156159
self.exporter_config.endpoint.as_str(),
@@ -373,10 +376,10 @@ fn build_endpoint_uri(endpoint: &str, path: &str) -> Result<Uri, crate::Error> {
373376
}
374377

375378
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#endpoint-urls-for-otlphttp
376-
fn resolve_endpoint(
379+
fn resolve_http_endpoint(
377380
signal_endpoint_var: &str,
378381
signal_endpoint_path: &str,
379-
provided_or_default_endpoint: &str,
382+
provided_endpoint: &str,
380383
) -> Result<Uri, crate::Error> {
381384
// per signal env var is not modified
382385
if let Some(endpoint) = env::var(signal_endpoint_var)
@@ -394,8 +397,14 @@ fn resolve_endpoint(
394397
return Ok(endpoint);
395398
}
396399

397-
// if neither works, we use the one provided in pipeline. If user never provides one, we will use the default one
398-
build_endpoint_uri(provided_or_default_endpoint, signal_endpoint_path)
400+
if provided_endpoint.is_empty() {
401+
build_endpoint_uri(
402+
OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
403+
signal_endpoint_path,
404+
)
405+
} else {
406+
provided_endpoint.parse().map_err(From::from)
407+
}
399408
}
400409

401410
#[allow(clippy::mutable_key_type)] // http headers are not mutated
@@ -411,16 +420,19 @@ fn add_header_from_string(input: &str, headers: &mut HashMap<HeaderName, HeaderV
411420
#[cfg(test)]
412421
mod tests {
413422
use crate::exporter::tests::run_env_test;
414-
use crate::{OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT};
423+
use crate::{
424+
new_exporter, WithExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT,
425+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
426+
};
415427

416-
use super::build_endpoint_uri;
428+
use super::{build_endpoint_uri, resolve_http_endpoint};
417429

418430
#[test]
419431
fn test_append_signal_path_to_generic_env() {
420432
run_env_test(
421433
vec![(OTEL_EXPORTER_OTLP_ENDPOINT, "http://example.com")],
422434
|| {
423-
let endpoint = super::resolve_endpoint(
435+
let endpoint = resolve_http_endpoint(
424436
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
425437
"/v1/traces",
426438
"http://localhost:4317",
@@ -436,7 +448,7 @@ mod tests {
436448
run_env_test(
437449
vec![(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, "http://example.com")],
438450
|| {
439-
let endpoint = super::resolve_endpoint(
451+
let endpoint = super::resolve_http_endpoint(
440452
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
441453
"/v1/traces",
442454
"http://localhost:4317",
@@ -455,7 +467,7 @@ mod tests {
455467
(OTEL_EXPORTER_OTLP_ENDPOINT, "http://wrong.com"),
456468
],
457469
|| {
458-
let endpoint = super::resolve_endpoint(
470+
let endpoint = super::resolve_http_endpoint(
459471
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
460472
"/v1/traces",
461473
"http://localhost:4317",
@@ -469,10 +481,13 @@ mod tests {
469481
#[test]
470482
fn test_use_provided_or_default_when_others_missing() {
471483
run_env_test(vec![], || {
472-
let endpoint =
473-
super::resolve_endpoint("NON_EXISTENT_VAR", "/v1/traces", "http://localhost:4317")
474-
.unwrap();
475-
assert_eq!(endpoint, "http://localhost:4317/v1/traces");
484+
let endpoint = super::resolve_http_endpoint(
485+
"NON_EXISTENT_VAR",
486+
"/v1/traces",
487+
"http://localhost:4317",
488+
)
489+
.unwrap();
490+
assert_eq!(endpoint, "http://localhost:4317/");
476491
});
477492
}
478493

@@ -501,7 +516,7 @@ mod tests {
501516
(OTEL_EXPORTER_OTLP_ENDPOINT, "http://example.com"),
502517
],
503518
|| {
504-
let endpoint = super::resolve_endpoint(
519+
let endpoint = super::resolve_http_endpoint(
505520
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
506521
"/v1/traces",
507522
"http://localhost:4317",
@@ -515,7 +530,7 @@ mod tests {
515530
#[test]
516531
fn test_all_invalid_urls_falls_back_to_error() {
517532
run_env_test(vec![], || {
518-
let result = super::resolve_endpoint(
533+
let result = super::resolve_http_endpoint(
519534
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
520535
"/v1/traces",
521536
"-*/*-/*-//-/-/yet-another-invalid-uri",
@@ -610,4 +625,37 @@ mod tests {
610625
}
611626
}
612627
}
628+
629+
#[test]
630+
fn test_http_exporter_endpoint() {
631+
// default endpoint should add signal path
632+
run_env_test(vec![], || {
633+
let exporter = new_exporter().http();
634+
635+
let url = resolve_http_endpoint(
636+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
637+
"/v1/traces",
638+
exporter.exporter_config.endpoint.as_str(),
639+
)
640+
.unwrap();
641+
642+
assert_eq!(url, "http://localhost:4318/v1/traces");
643+
});
644+
645+
// if builder endpoint is set, it should not add signal path
646+
run_env_test(vec![], || {
647+
let exporter = new_exporter()
648+
.http()
649+
.with_endpoint("http://localhost:4318/v1/tracesbutnotreally");
650+
651+
let url = resolve_http_endpoint(
652+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
653+
"/v1/traces",
654+
exporter.exporter_config.endpoint.as_str(),
655+
)
656+
.unwrap();
657+
658+
assert_eq!(url, "http://localhost:4318/v1/tracesbutnotreally");
659+
});
660+
}
613661
}

opentelemetry-otlp/src/exporter/mod.rs

+9-20
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub const OTEL_EXPORTER_OTLP_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_TIMEOUT";
5858
pub const OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT: u64 = 10;
5959

6060
// Endpoints per protocol https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md
61+
#[cfg(feature = "grpc-tonic")]
6162
const OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT: &str = "http://localhost:4317";
6263
const OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT: &str = "http://localhost:4318";
6364

@@ -69,7 +70,8 @@ pub(crate) mod tonic;
6970
/// Configuration for the OTLP exporter.
7071
#[derive(Debug)]
7172
pub struct ExportConfig {
72-
/// The base address of the OTLP collector. If not set, the default address is used.
73+
/// The address of the OTLP collector. If it's not provided via builder or environment variables.
74+
/// Default address will be used based on the protocol.
7375
pub endpoint: String,
7476

7577
/// The protocol to use when communicating with the collector.
@@ -84,7 +86,9 @@ impl Default for ExportConfig {
8486
let protocol = default_protocol();
8587

8688
ExportConfig {
87-
endpoint: default_endpoint(protocol),
89+
endpoint: "".to_string(),
90+
// don't use default_endpoint(protocol) here otherwise we
91+
// won't know if user provided a value
8892
protocol,
8993
timeout: Duration::from_secs(OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT),
9094
}
@@ -128,15 +132,6 @@ fn default_protocol() -> Protocol {
128132
}
129133
}
130134

131-
/// default endpoint for protocol
132-
fn default_endpoint(protocol: Protocol) -> String {
133-
match protocol {
134-
Protocol::Grpc => OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT.to_string(),
135-
Protocol::HttpBinary => OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT.to_string(),
136-
Protocol::HttpJson => OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT.to_string(),
137-
}
138-
}
139-
140135
/// default user-agent headers
141136
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
142137
fn default_headers() -> std::collections::HashMap<String, String> {
@@ -183,7 +178,7 @@ impl HasExportConfig for HttpExporterBuilder {
183178
/// # }
184179
/// ```
185180
pub trait WithExportConfig {
186-
/// Set the address of the OTLP collector. If not set, the default address is used.
181+
/// Set the address of the OTLP collector. If not set or set to empty string, the default address is used.
187182
fn with_endpoint<T: Into<String>>(self, endpoint: T) -> Self;
188183
/// Set the protocol to use when communicating with the collector.
189184
///
@@ -297,21 +292,15 @@ mod tests {
297292
fn test_default_http_endpoint() {
298293
let exporter_builder = crate::new_exporter().http();
299294

300-
assert_eq!(
301-
exporter_builder.exporter_config.endpoint,
302-
"http://localhost:4318"
303-
);
295+
assert_eq!(exporter_builder.exporter_config.endpoint, "");
304296
}
305297

306298
#[cfg(feature = "grpc-tonic")]
307299
#[test]
308300
fn test_default_tonic_endpoint() {
309301
let exporter_builder = crate::new_exporter().tonic();
310302

311-
assert_eq!(
312-
exporter_builder.exporter_config.endpoint,
313-
"http://localhost:4317"
314-
);
303+
assert_eq!(exporter_builder.exporter_config.endpoint, "");
315304
}
316305

317306
#[test]

opentelemetry-otlp/src/exporter/tonic/mod.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use tonic::transport::Channel;
1111
#[cfg(feature = "tls")]
1212
use tonic::transport::ClientTlsConfig;
1313

14-
use super::{default_headers, parse_header_string};
14+
use super::{default_headers, parse_header_string, OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT};
1515
use crate::exporter::Compression;
1616
use crate::{
1717
ExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_ENDPOINT,
@@ -149,7 +149,6 @@ impl Default for TonicExporterBuilder {
149149
TonicExporterBuilder {
150150
exporter_config: ExportConfig {
151151
protocol: crate::Protocol::Grpc,
152-
endpoint: crate::exporter::default_endpoint(crate::Protocol::Grpc),
153152
..Default::default()
154153
},
155154
tonic_config,
@@ -213,7 +212,6 @@ impl TonicExporterBuilder {
213212
fn build_channel(
214213
self,
215214
signal_endpoint_var: &str,
216-
signal_endpoint_path: &str,
217215
signal_timeout_var: &str,
218216
signal_compression_var: &str,
219217
signal_headers_var: &str,
@@ -255,12 +253,24 @@ impl TonicExporterBuilder {
255253
}
256254

257255
let config = self.exporter_config;
256+
257+
// resolving endpoint string
258+
// grpc doesn't have a "path" like http(See https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md)
259+
// the path of grpc calls are based on the protobuf service definition
260+
// so we won't append one for default grpc endpoints
261+
// If users for some reason want to use a custom path, they can use env var or builder to pass it
258262
let endpoint = match env::var(signal_endpoint_var)
259263
.ok()
260264
.or(env::var(OTEL_EXPORTER_OTLP_ENDPOINT).ok())
261265
{
262266
Some(val) => val,
263-
None => format!("{}{signal_endpoint_path}", config.endpoint),
267+
None => {
268+
if config.endpoint.is_empty() {
269+
OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT.to_string()
270+
} else {
271+
config.endpoint
272+
}
273+
}
264274
};
265275

266276
let endpoint = Channel::from_shared(endpoint).map_err(crate::Error::from)?;
@@ -300,7 +310,6 @@ impl TonicExporterBuilder {
300310

301311
let (channel, interceptor, compression) = self.build_channel(
302312
crate::logs::OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
303-
"/v1/logs",
304313
crate::logs::OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
305314
crate::logs::OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
306315
crate::logs::OTEL_EXPORTER_OTLP_LOGS_HEADERS,
@@ -323,7 +332,6 @@ impl TonicExporterBuilder {
323332

324333
let (channel, interceptor, compression) = self.build_channel(
325334
crate::metric::OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
326-
"/v1/metrics",
327335
crate::metric::OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
328336
crate::metric::OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
329337
crate::metric::OTEL_EXPORTER_OTLP_METRICS_HEADERS,
@@ -347,7 +355,6 @@ impl TonicExporterBuilder {
347355

348356
let (channel, interceptor, compression) = self.build_channel(
349357
crate::span::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
350-
"/v1/traces",
351358
crate::span::OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
352359
crate::span::OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
353360
crate::span::OTEL_EXPORTER_OTLP_TRACES_HEADERS,

0 commit comments

Comments
 (0)