Skip to content

Commit f4f8d58

Browse files
authored
fix(datadog): parse the endpoint to remove tailing slash. (open-telemetry#787)
1 parent fdf6401 commit f4f8d58

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

opentelemetry-datadog/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ opentelemetry = { version = "0.17", path = "../opentelemetry", features = ["trac
2929
opentelemetry-http = { version = "0.6", path = "../opentelemetry-http" }
3030
opentelemetry-semantic-conventions = { version = "0.9", path = "../opentelemetry-semantic-conventions" }
3131
rmp = "0.8"
32+
url = "2.2"
3233
reqwest = { version = "0.11", default-features = false, optional = true }
3334
surf = { version = "2.0", default-features = false, optional = true }
3435
thiserror = "1.0"

opentelemetry-datadog/src/exporter/mod.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use opentelemetry_http::{HttpClient, ResponseExt};
2323
use opentelemetry_semantic_conventions as semcov;
2424
use std::sync::Arc;
2525
use std::time::Duration;
26+
use url::Url;
2627

2728
/// Default Datadog collector endpoint
2829
const DEFAULT_AGENT_ENDPOINT: &str = "http://127.0.0.1:8126";
@@ -197,6 +198,25 @@ impl DatadogPipelineBuilder {
197198
}
198199
}
199200

201+
// parse the endpoint and append the path based on versions.
202+
// keep the query and host the same.
203+
fn build_endpoint(agent_endpoint: &str, version: &str) -> Result<Uri, TraceError> {
204+
// build agent endpoint based on version
205+
let mut endpoint = agent_endpoint
206+
.parse::<Url>()
207+
.map_err::<Error, _>(Into::into)?;
208+
let mut paths = endpoint
209+
.path_segments()
210+
.map(|c| c.filter(|s| !s.is_empty()).collect::<Vec<_>>())
211+
.unwrap_or_default();
212+
paths.push(version);
213+
214+
let path_str = paths.join("/");
215+
endpoint.set_path(path_str.as_str());
216+
217+
Ok(endpoint.as_str().parse().map_err::<Error, _>(Into::into)?)
218+
}
219+
200220
fn build_exporter_with_service_name(
201221
self,
202222
service_name: String,
@@ -206,10 +226,10 @@ impl DatadogPipelineBuilder {
206226
service_name,
207227
..Default::default()
208228
};
209-
let endpoint = self.agent_endpoint + self.version.path();
229+
210230
let exporter = DatadogExporter::new(
211231
model_config,
212-
endpoint.parse().map_err::<Error, _>(Into::into)?,
232+
Self::build_endpoint(&self.agent_endpoint, self.version.path())?,
213233
self.version,
214234
client,
215235
self.resource_mapping,
@@ -266,7 +286,9 @@ impl DatadogPipelineBuilder {
266286
self
267287
}
268288

269-
/// Assign the Datadog collector endpoint
289+
/// Assign the Datadog collector endpoint.
290+
///
291+
/// The endpoint of the datadog agent, by default it is `http://127.0.0.1:8126`.
270292
pub fn with_agent_endpoint<T: Into<String>>(mut self, endpoint: T) -> Self {
271293
self.agent_endpoint = endpoint.into();
272294
self
@@ -379,6 +401,7 @@ fn mapping_debug(f: &Option<FieldMapping>) -> String {
379401
#[cfg(test)]
380402
mod tests {
381403
use super::*;
404+
use crate::ApiVersion::Version05;
382405

383406
use crate::exporter::model::tests::get_span;
384407

@@ -396,4 +419,34 @@ mod tests {
396419

397420
assert_eq!(traces, expected);
398421
}
422+
423+
#[test]
424+
fn test_agent_endpoint_with_version() {
425+
let with_tail_slash =
426+
DatadogPipelineBuilder::build_endpoint("http://localhost:8126/", Version05.path());
427+
let without_tail_slash =
428+
DatadogPipelineBuilder::build_endpoint("http://localhost:8126", Version05.path());
429+
let with_query = DatadogPipelineBuilder::build_endpoint(
430+
"http://localhost:8126?api_key=123",
431+
Version05.path(),
432+
);
433+
let invalid = DatadogPipelineBuilder::build_endpoint(
434+
"http://localhost:klsajfjksfh",
435+
Version05.path(),
436+
);
437+
438+
assert_eq!(
439+
with_tail_slash.unwrap().to_string(),
440+
"http://localhost:8126/v0.5/traces"
441+
);
442+
assert_eq!(
443+
without_tail_slash.unwrap().to_string(),
444+
"http://localhost:8126/v0.5/traces"
445+
);
446+
assert_eq!(
447+
with_query.unwrap().to_string(),
448+
"http://localhost:8126/v0.5/traces?api_key=123"
449+
);
450+
assert!(invalid.is_err())
451+
}
399452
}

opentelemetry-datadog/src/exporter/model/mod.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use crate::exporter::ModelConfig;
2+
use http::uri;
23
use opentelemetry::sdk::export::{
34
trace::{self, SpanData},
45
ExportError,
56
};
67
use std::fmt::Debug;
8+
use url::ParseError;
79

810
mod v03;
911
mod v05;
@@ -73,8 +75,8 @@ pub enum Error {
7375
#[error(transparent)]
7476
RequestError(#[from] http::Error),
7577
/// The Uri was invalid
76-
#[error(transparent)]
77-
InvalidUri(#[from] http::uri::InvalidUri),
78+
#[error("invalid url {0}")]
79+
InvalidUri(String),
7880
/// Other errors
7981
#[error("{0}")]
8082
Other(String),
@@ -92,6 +94,18 @@ impl From<rmp::encode::ValueWriteError> for Error {
9294
}
9395
}
9496

97+
impl From<url::ParseError> for Error {
98+
fn from(err: ParseError) -> Self {
99+
Self::InvalidUri(err.to_string())
100+
}
101+
}
102+
103+
impl From<uri::InvalidUri> for Error {
104+
fn from(err: uri::InvalidUri) -> Self {
105+
Self::InvalidUri(err.to_string())
106+
}
107+
}
108+
95109
/// Version of datadog trace ingestion API
96110
#[derive(Debug, Copy, Clone)]
97111
#[non_exhaustive]

opentelemetry-stackdriver/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ gcp_auth = { version = "0.7", optional = true }
1515
hex = "0.4"
1616
http = "0.2"
1717
hyper = "0.14.2"
18-
hyper-rustls = { version = "0.22.1", optional = true }
18+
hyper-rustls = { version = "0.23", optional = true }
1919
opentelemetry = { version = "0.17", path = "../opentelemetry" }
2020
opentelemetry-semantic-conventions = { version = "0.9", path = "../opentelemetry-semantic-conventions" }
2121
prost = "0.9"

0 commit comments

Comments
 (0)