From 1482d5ca6a9ec80aef265acd6c7dc66561456deb Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Mon, 26 Feb 2024 22:10:39 -0500 Subject: [PATCH 01/22] Add http-json feature flag --- opentelemetry-otlp/Cargo.toml | 5 +++- .../examples/basic-otlp-http/Cargo.toml | 2 +- opentelemetry-otlp/src/exporter/http/mod.rs | 2 +- opentelemetry-otlp/src/exporter/http/trace.rs | 8 +++--- opentelemetry-otlp/src/exporter/mod.rs | 28 +++++++++++-------- opentelemetry-otlp/src/lib.rs | 23 +++++++-------- opentelemetry-otlp/src/span.rs | 8 +++--- 7 files changed, 43 insertions(+), 33 deletions(-) diff --git a/opentelemetry-otlp/Cargo.toml b/opentelemetry-otlp/Cargo.toml index 7224392543..70668cf2cd 100644 --- a/opentelemetry-otlp/Cargo.toml +++ b/opentelemetry-otlp/Cargo.toml @@ -42,6 +42,7 @@ reqwest = { workspace = true, optional = true } http = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"], optional = true } thiserror = { workspace = true } +serde_json = { workspace = true, optional = true } [dev-dependencies] tokio-stream = { workspace = true, features = ["net"] } @@ -58,7 +59,7 @@ metrics = ["opentelemetry/metrics", "opentelemetry_sdk/metrics", "opentelemetry- logs = ["opentelemetry/logs", "opentelemetry_sdk/logs", "opentelemetry-proto/logs"] # add ons -serialize = ["serde"] +serialize = ["serde", "serde_json"] default = ["grpc-tonic", "trace"] @@ -73,6 +74,8 @@ http-proto = ["prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-mess reqwest-blocking-client = ["reqwest/blocking", "opentelemetry-http/reqwest"] reqwest-client = ["reqwest", "opentelemetry-http/reqwest"] reqwest-rustls = ["reqwest", "reqwest/rustls-tls-native-roots"] +# http json +http-json = ["serde_json", "prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "http", "trace", "metrics"] # test integration-testing = ["tonic", "prost", "tokio/full", "trace"] diff --git a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml index 496dfc70b1..4530074a79 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml +++ b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml @@ -9,7 +9,7 @@ publish = false once_cell = { workspace = true } opentelemetry = { path = "../../../opentelemetry" } opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio", "metrics", "logs"] } -opentelemetry-otlp = { path = "../..", features = ["http-proto", "reqwest-client", "logs"] } +opentelemetry-otlp = { path = "../..", features = ["http-proto", "http-json", "reqwest-client", "logs"] } opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing", default-features = false} opentelemetry-semantic-conventions = { path = "../../../opentelemetry-semantic-conventions" } diff --git a/opentelemetry-otlp/src/exporter/http/mod.rs b/opentelemetry-otlp/src/exporter/http/mod.rs index 6cd90203a7..ef64662038 100644 --- a/opentelemetry-otlp/src/exporter/http/mod.rs +++ b/opentelemetry-otlp/src/exporter/http/mod.rs @@ -22,7 +22,7 @@ mod logs; mod trace; /// Configuration of the http transport -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] #[derive(Debug)] #[cfg_attr( all( diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index e824978b57..e9a8606c88 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -68,7 +68,7 @@ impl SpanExporter for OtlpHttpClient { } } -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; use prost::Message; @@ -79,12 +79,12 @@ fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { let mut buf = vec![]; req.encode(&mut buf).map_err(crate::Error::from)?; - Ok((buf, "application/x-protobuf")) + Ok((buf, "application/x-protobuf")) } -#[cfg(not(feature = "http-proto"))] +#[cfg(not(any(feature = "http-proto", feature = "http-json")))] fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { Err(TraceError::Other( - "No http protocol configured. Enable one via `http-proto`".into(), + "No http protocol configured. Enable one via `http-proto` or `http-json`".into(), )) } diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 668474d798..6403222446 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -2,7 +2,7 @@ //! //! OTLP supports sending data via different protocols and formats. -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] use crate::exporter::http::HttpExporterBuilder; #[cfg(feature = "grpc-tonic")] use crate::exporter::tonic::TonicExporterBuilder; @@ -31,15 +31,19 @@ pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION #[cfg(feature = "http-proto")] /// Default protocol, using http-proto. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; -#[cfg(all(feature = "grpc-tonic", not(feature = "http-proto")))] -/// Default protocol, using grpc as http-proto feature is not enabled. +#[cfg(all(feature = "http-json", feature = "trace", not(any(feature = "http-proto", feature = "grpc-tonic"))))] +/// Default protocol, using http-json. +pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; +#[cfg(all(feature = "grpc-tonic", not(any(feature = "http-proto", feature = "http-json"))))] +/// Default protocol, using grpc as http-proto or http-json feature is not enabled. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; -#[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto"))))] +#[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))))] /// Default protocol if no features are enabled. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = ""; const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF: &str = "http/protobuf"; const OTEL_EXPORTER_OTLP_PROTOCOL_GRPC: &str = "grpc"; +const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON: &str = "http/json"; /// Max waiting time for the backend to process each signal batch, defaults to 10 seconds. pub const OTEL_EXPORTER_OTLP_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_TIMEOUT"; @@ -50,7 +54,7 @@ pub const OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT: u64 = 10; const OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT: &str = "http://localhost:4317"; const OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT: &str = "http://localhost:4318"; -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] pub(crate) mod http; #[cfg(feature = "grpc-tonic")] pub(crate) mod tonic; @@ -112,6 +116,7 @@ fn default_protocol() -> Protocol { match OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT { OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF => Protocol::HttpBinary, OTEL_EXPORTER_OTLP_PROTOCOL_GRPC => Protocol::Grpc, + OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON => Protocol::HttpJson, _ => Protocol::HttpBinary, } } @@ -121,11 +126,12 @@ fn default_endpoint(protocol: Protocol) -> String { match protocol { Protocol::Grpc => OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT.to_string(), Protocol::HttpBinary => OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT.to_string(), + Protocol::HttpJson => OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT.to_string(), } } /// default user-agent headers -#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] +#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] fn default_headers() -> std::collections::HashMap { let mut headers = std::collections::HashMap::new(); headers.insert( @@ -148,7 +154,7 @@ impl HasExportConfig for TonicExporterBuilder { } } -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] impl HasExportConfig for HttpExporterBuilder { fn export_config(&mut self) -> &mut ExportConfig { &mut self.exporter_config @@ -210,7 +216,7 @@ impl WithExportConfig for B { } } -#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] +#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] fn parse_header_string(value: &str) -> impl Iterator { value .split_terminator(',') @@ -218,7 +224,7 @@ fn parse_header_string(value: &str) -> impl Iterator { .filter_map(parse_header_key_value_string) } -#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] +#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] fn parse_header_key_value_string(key_value_string: &str) -> Option<(&str, &str)> { key_value_string .split_once('=') @@ -227,7 +233,7 @@ fn parse_header_key_value_string(key_value_string: &str) -> Option<(&str, &str)> } #[cfg(test)] -#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] +#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] mod tests { pub(crate) fn run_env_test(env_vars: T, f: F) @@ -245,7 +251,7 @@ mod tests { ) } - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", feature = "http-json"))] #[test] fn test_default_http_endpoint() { let exporter_builder = crate::new_exporter().http(); diff --git a/opentelemetry-otlp/src/lib.rs b/opentelemetry-otlp/src/lib.rs index 0b96ae039d..b04cc30a49 100644 --- a/opentelemetry-otlp/src/lib.rs +++ b/opentelemetry-otlp/src/lib.rs @@ -104,6 +104,7 @@ //! * `reqwest-blocking-client`: Use reqwest blocking http client. //! * `reqwest-client`: Use reqwest http client. //! * `reqwest-rustls`: Use reqwest with TLS. +//! * `http-json`: Use http as transport layer, JSON as body format. //! //! # Kitchen Sink Full Configuration //! @@ -247,7 +248,7 @@ pub use crate::exporter::{ use opentelemetry_sdk::export::ExportError; -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] pub use crate::exporter::http::HttpExporterBuilder; #[cfg(feature = "grpc-tonic")] @@ -278,7 +279,7 @@ impl OtlpExporterPipeline { /// and build the exporter. /// /// This exporter can be used in both `tracing` and `metrics` pipeline. - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", feature = "http-json"))] pub fn http(self) -> HttpExporterBuilder { HttpExporterBuilder::default() } @@ -314,7 +315,7 @@ pub enum Error { Transport(#[from] tonic::transport::Error), /// Wrap the [`tonic::codegen::http::uri::InvalidUri`] error - #[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] + #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] #[error("invalid URI {0}")] InvalidUri(#[from] http::uri::InvalidUri), @@ -329,32 +330,32 @@ pub enum Error { }, /// Http requests failed because no http client is provided. - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", feature = "http-json"))] #[error( "no http client, you must select one from features or provide your own implementation" )] NoHttpClient, /// Http requests failed. - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", feature = "http-json"))] #[error("http request failed with {0}")] RequestFailed(#[from] opentelemetry_http::HttpError), /// The provided value is invalid in HTTP headers. - #[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] + #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] #[error("http header value error {0}")] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), /// The provided name is invalid in HTTP headers. - #[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] + #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] #[error("http header name error {0}")] InvalidHeaderName(#[from] http::header::InvalidHeaderName), /// Prost encode failed - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", all(feature = "http-json", not(feature = "trace"))))] #[error("prost encoding error {0}")] EncodeError(#[from] prost::EncodeError), - + /// The lock in exporters has been poisoned. #[cfg(feature = "metrics")] #[error("the lock of the {0} has been poisoned")] @@ -393,10 +394,10 @@ impl ExportError for Error { pub enum Protocol { /// GRPC protocol Grpc, - // TODO add support for other protocols - // HttpJson, /// HTTP protocol with binary protobuf HttpBinary, + /// HTTP protocol with JSON payload + HttpJson, } #[derive(Debug, Default)] diff --git a/opentelemetry-otlp/src/span.rs b/opentelemetry-otlp/src/span.rs index 76383f2f04..3b0a543e31 100644 --- a/opentelemetry-otlp/src/span.rs +++ b/opentelemetry-otlp/src/span.rs @@ -19,7 +19,7 @@ use sdk::runtime::RuntimeChannel; #[cfg(feature = "grpc-tonic")] use crate::exporter::tonic::TonicExporterBuilder; -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] use crate::exporter::http::HttpExporterBuilder; use crate::{NoExporterConfig, OtlpPipeline}; @@ -187,7 +187,7 @@ pub enum SpanExporterBuilder { #[cfg(feature = "grpc-tonic")] Tonic(TonicExporterBuilder), /// Http span exporter builder - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", feature = "http-json"))] Http(HttpExporterBuilder), } @@ -197,7 +197,7 @@ impl SpanExporterBuilder { match self { #[cfg(feature = "grpc-tonic")] SpanExporterBuilder::Tonic(builder) => builder.build_span_exporter(), - #[cfg(feature = "http-proto")] + #[cfg(any(feature = "http-proto", feature = "http-json"))] SpanExporterBuilder::Http(builder) => builder.build_span_exporter(), } } @@ -210,7 +210,7 @@ impl From for SpanExporterBuilder { } } -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] impl From for SpanExporterBuilder { fn from(exporter: HttpExporterBuilder) -> Self { SpanExporterBuilder::Http(exporter) From 576a56d18afc8331942bc0e011e78711c88fca40 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Tue, 27 Feb 2024 16:26:13 -0500 Subject: [PATCH 02/22] prototype json serialization --- opentelemetry-otlp/Cargo.toml | 2 +- .../src/exporter/http/metrics.rs | 2 +- opentelemetry-otlp/src/exporter/http/trace.rs | 18 +++++++++++++----- opentelemetry-otlp/src/exporter/mod.rs | 6 +++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/opentelemetry-otlp/Cargo.toml b/opentelemetry-otlp/Cargo.toml index 70668cf2cd..84163d8be6 100644 --- a/opentelemetry-otlp/Cargo.toml +++ b/opentelemetry-otlp/Cargo.toml @@ -31,7 +31,7 @@ futures-core = { workspace = true } opentelemetry = { version = "0.22", default-features = false, path = "../opentelemetry" } opentelemetry_sdk = { version = "0.22", default-features = false, path = "../opentelemetry-sdk" } opentelemetry-http = { version = "0.11", path = "../opentelemetry-http", optional = true } -opentelemetry-proto = { version = "0.5", path = "../opentelemetry-proto", default-features = false } +opentelemetry-proto = { version = "0.5", path = "../opentelemetry-proto", default-features = false, features=["with-serde"] } opentelemetry-semantic-conventions = { version = "0.14", path = "../opentelemetry-semantic-conventions" } prost = { workspace = true, optional = true } diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index b5ec92eb63..6bcdb99f42 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -61,7 +61,7 @@ fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> } #[cfg(not(feature = "http-proto"))] -fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { +fn build_body(_metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { Err(MetricsError::Other( "No http protocol configured. Enable one via `http-proto`".into(), )) diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index e9a8606c88..a715ed1e89 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -71,15 +71,23 @@ impl SpanExporter for OtlpHttpClient { #[cfg(any(feature = "http-proto", feature = "http-json"))] fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; - use prost::Message; let req = ExportTraceServiceRequest { resource_spans: spans.into_iter().map(Into::into).collect(), }; - let mut buf = vec![]; - req.encode(&mut buf).map_err(crate::Error::from)?; - - Ok((buf, "application/x-protobuf")) + let buf; + let ctype; + #[cfg(all(feature = "http-proto", not(feature = "http-json")))]{ + use prost::Message; + buf = req.encode_to_vec(); + ctype = "application/x-protobuf"; + } + #[cfg(all(feature = "http-json", not(feature = "http=proto")))]{ + let json_struct = serde_json::to_string_pretty(&req).unwrap(); + buf = json_struct.into(); + ctype = "application/json"; + } + Ok((buf, ctype)) } #[cfg(not(any(feature = "http-proto", feature = "http-json")))] diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 6403222446..80e52eb106 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,13 +28,13 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", not(all(feature = "http-json", feature = "trace"))))] /// Default protocol, using http-proto. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; -#[cfg(all(feature = "http-json", feature = "trace", not(any(feature = "http-proto", feature = "grpc-tonic"))))] +#[cfg(all(feature = "http-json", feature = "trace", not(feature = "http-proto")))] /// Default protocol, using http-json. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; -#[cfg(all(feature = "grpc-tonic", not(any(feature = "http-proto", feature = "http-json"))))] +#[cfg(all(feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json", feature = "trace"))))] /// Default protocol, using grpc as http-proto or http-json feature is not enabled. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; #[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))))] From d13ab4b22b2ffe4caa887ec9f17bd8fc7d474e01 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Tue, 27 Feb 2024 16:55:52 -0500 Subject: [PATCH 03/22] fix default protocol --- opentelemetry-otlp/src/exporter/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 80e52eb106..06b76a7c96 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,13 +28,13 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; -#[cfg(any(feature = "http-proto", not(all(feature = "http-json", feature = "trace"))))] -/// Default protocol, using http-proto. -pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; -#[cfg(all(feature = "http-json", feature = "trace", not(feature = "http-proto")))] +#[cfg(all(feature = "http-json", feature = "trace", not(all(feature = "http-proto", feature = "grpc-toni"))))] /// Default protocol, using http-json. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; -#[cfg(all(feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json", feature = "trace"))))] +#[cfg(all(feature = "http-proto", not(all(feature = "http-json", feature = "trace"))))] +/// Default protocol, using http-proto. +pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; +#[cfg(all(feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json"))))] /// Default protocol, using grpc as http-proto or http-json feature is not enabled. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; #[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))))] From ed4fc0346ceb4088075786bc7d766bab76f0fb4a Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Tue, 27 Feb 2024 17:11:16 -0500 Subject: [PATCH 04/22] fix typo --- opentelemetry-otlp/src/exporter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 06b76a7c96..8f777a8821 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,10 +28,10 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; -#[cfg(all(feature = "http-json", feature = "trace", not(all(feature = "http-proto", feature = "grpc-toni"))))] +#[cfg(all(feature = "trace", not(feature = "http-proto"), not(feature = "grpc-tonic")))] /// Default protocol, using http-json. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; -#[cfg(all(feature = "http-proto", not(all(feature = "http-json", feature = "trace"))))] +#[cfg(feature = "http-proto")] /// Default protocol, using http-proto. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; #[cfg(all(feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json"))))] From 587c341f6b42109108cefdeb0d43cbbda7c69e9b Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Tue, 27 Feb 2024 20:37:43 -0500 Subject: [PATCH 05/22] use default protocol for HttpConfig --- opentelemetry-otlp/src/exporter/http/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/http/mod.rs b/opentelemetry-otlp/src/exporter/http/mod.rs index ef64662038..c7042a0f13 100644 --- a/opentelemetry-otlp/src/exporter/http/mod.rs +++ b/opentelemetry-otlp/src/exporter/http/mod.rs @@ -1,5 +1,5 @@ use crate::{ - ExportConfig, Protocol, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, + ExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT, }; use http::{HeaderName, HeaderValue, Uri}; @@ -10,7 +10,7 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; -use super::{default_headers, parse_header_string}; +use super::{default_headers, default_protocol, parse_header_string}; #[cfg(feature = "metrics")] mod metrics; @@ -98,7 +98,7 @@ impl Default for HttpExporterBuilder { fn default() -> Self { HttpExporterBuilder { exporter_config: ExportConfig { - protocol: Protocol::HttpBinary, + protocol: default_protocol(), ..ExportConfig::default() }, http_config: HttpConfig { From d460bda47217b73408b670842f1f4c6a5476543a Mon Sep 17 00:00:00 2001 From: Ramji Date: Wed, 28 Feb 2024 12:24:32 -0500 Subject: [PATCH 06/22] Update opentelemetry-otlp/src/exporter/http/trace.rs Co-authored-by: Zhongyang Wu --- opentelemetry-otlp/src/exporter/http/trace.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index a715ed1e89..65aab88d2f 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -82,7 +82,7 @@ fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { buf = req.encode_to_vec(); ctype = "application/x-protobuf"; } - #[cfg(all(feature = "http-json", not(feature = "http=proto")))]{ + #[cfg(all(feature = "http-json", not(feature = "http-proto")))]{ let json_struct = serde_json::to_string_pretty(&req).unwrap(); buf = json_struct.into(); ctype = "application/json"; From da76734f0b04b8464e43785f2da981bafa010ac2 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Thu, 29 Feb 2024 16:20:12 -0500 Subject: [PATCH 07/22] address review comments --- opentelemetry-otlp/Cargo.toml | 4 ++-- opentelemetry-otlp/src/exporter/http/trace.rs | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/opentelemetry-otlp/Cargo.toml b/opentelemetry-otlp/Cargo.toml index 84163d8be6..d1fcd2025f 100644 --- a/opentelemetry-otlp/Cargo.toml +++ b/opentelemetry-otlp/Cargo.toml @@ -31,7 +31,7 @@ futures-core = { workspace = true } opentelemetry = { version = "0.22", default-features = false, path = "../opentelemetry" } opentelemetry_sdk = { version = "0.22", default-features = false, path = "../opentelemetry-sdk" } opentelemetry-http = { version = "0.11", path = "../opentelemetry-http", optional = true } -opentelemetry-proto = { version = "0.5", path = "../opentelemetry-proto", default-features = false, features=["with-serde"] } +opentelemetry-proto = { version = "0.5", path = "../opentelemetry-proto", default-features = false } opentelemetry-semantic-conventions = { version = "0.14", path = "../opentelemetry-semantic-conventions" } prost = { workspace = true, optional = true } @@ -75,7 +75,7 @@ reqwest-blocking-client = ["reqwest/blocking", "opentelemetry-http/reqwest"] reqwest-client = ["reqwest", "opentelemetry-http/reqwest"] reqwest-rustls = ["reqwest", "reqwest/rustls-tls-native-roots"] # http json -http-json = ["serde_json", "prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "http", "trace", "metrics"] +http-json = ["serde_json", "prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "opentelemetry-proto/with-serde", "http", "trace", "metrics"] # test integration-testing = ["tonic", "prost", "tokio/full", "trace"] diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index 65aab88d2f..f472f157a0 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -71,22 +71,26 @@ impl SpanExporter for OtlpHttpClient { #[cfg(any(feature = "http-proto", feature = "http-json"))] fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; + use prost::Message; + + use crate::{exporter::default_protocol, Protocol}; let req = ExportTraceServiceRequest { resource_spans: spans.into_iter().map(Into::into).collect(), }; let buf; let ctype; - #[cfg(all(feature = "http-proto", not(feature = "http-json")))]{ - use prost::Message; - buf = req.encode_to_vec(); - ctype = "application/x-protobuf"; - } - #[cfg(all(feature = "http-json", not(feature = "http-proto")))]{ - let json_struct = serde_json::to_string_pretty(&req).unwrap(); - buf = json_struct.into(); - ctype = "application/json"; - } + match default_protocol() { + Protocol::HttpJson => { + let json_struct = serde_json::to_string_pretty(&req).unwrap(); + buf = json_struct.into(); + ctype = "application/json"; + }, + _ => { + buf = req.encode_to_vec(); + ctype = "application/x-protobuf"; + }, + }; Ok((buf, ctype)) } From aa74b9cf180edbaf96b580c65986836965e49004 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Thu, 29 Feb 2024 16:43:42 -0500 Subject: [PATCH 08/22] run pre-commit script --- opentelemetry-otlp/src/exporter/http/trace.rs | 15 +++++++++------ opentelemetry-otlp/src/exporter/mod.rs | 11 +++++++++-- opentelemetry-otlp/src/lib.rs | 7 +++++-- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index f472f157a0..66aa62e53d 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -70,26 +70,29 @@ impl SpanExporter for OtlpHttpClient { #[cfg(any(feature = "http-proto", feature = "http-json"))] fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { + use crate::exporter::default_protocol; + #[cfg(feature = "http-json")] + use crate::Protocol; use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; + #[cfg(feature = "http-proto")] use prost::Message; - use crate::{exporter::default_protocol, Protocol}; - let req = ExportTraceServiceRequest { resource_spans: spans.into_iter().map(Into::into).collect(), }; let buf; let ctype; match default_protocol() { + #[cfg(feature = "http-json")] Protocol::HttpJson => { let json_struct = serde_json::to_string_pretty(&req).unwrap(); buf = json_struct.into(); - ctype = "application/json"; - }, + ctype = "application/json"; + } _ => { buf = req.encode_to_vec(); - ctype = "application/x-protobuf"; - }, + ctype = "application/x-protobuf"; + } }; Ok((buf, ctype)) } diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 8f777a8821..ff99effe6f 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,13 +28,20 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; -#[cfg(all(feature = "trace", not(feature = "http-proto"), not(feature = "grpc-tonic")))] +#[cfg(all( + feature = "trace", + not(feature = "http-proto"), + not(feature = "grpc-tonic") +))] /// Default protocol, using http-json. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; #[cfg(feature = "http-proto")] /// Default protocol, using http-proto. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; -#[cfg(all(feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json"))))] +#[cfg(all( + feature = "grpc-tonic", + not(all(feature = "http-proto", feature = "http-json")) +))] /// Default protocol, using grpc as http-proto or http-json feature is not enabled. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; #[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))))] diff --git a/opentelemetry-otlp/src/lib.rs b/opentelemetry-otlp/src/lib.rs index b04cc30a49..cec17857b3 100644 --- a/opentelemetry-otlp/src/lib.rs +++ b/opentelemetry-otlp/src/lib.rs @@ -352,10 +352,13 @@ pub enum Error { InvalidHeaderName(#[from] http::header::InvalidHeaderName), /// Prost encode failed - #[cfg(any(feature = "http-proto", all(feature = "http-json", not(feature = "trace"))))] + #[cfg(any( + feature = "http-proto", + all(feature = "http-json", not(feature = "trace")) + ))] #[error("prost encoding error {0}")] EncodeError(#[from] prost::EncodeError), - + /// The lock in exporters has been poisoned. #[cfg(feature = "metrics")] #[error("the lock of the {0} has been poisoned")] From efa8588bda77f5d41ba9849e8fc01ae619e59736 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Thu, 29 Feb 2024 17:10:19 -0500 Subject: [PATCH 09/22] fix dependency --- opentelemetry-otlp/src/exporter/http/trace.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index 66aa62e53d..3052c41b11 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -74,7 +74,6 @@ fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { #[cfg(feature = "http-json")] use crate::Protocol; use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; - #[cfg(feature = "http-proto")] use prost::Message; let req = ExportTraceServiceRequest { From 04593ece4acb87569e903fc3df4e72d3af5ab6cb Mon Sep 17 00:00:00 2001 From: Ramji Date: Sun, 3 Mar 2024 18:15:26 -0500 Subject: [PATCH 10/22] Update mod.rs fix format --- opentelemetry-otlp/src/exporter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index bb6df67258..8c41174730 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -231,7 +231,7 @@ fn parse_header_string(value: &str) -> impl Iterator { .filter_map(parse_header_key_value_string) } -#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature="http-json"))] +#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] fn url_decode(value: &str) -> Option { let mut result = String::with_capacity(value.len()); let mut chars_to_decode = Vec::::new(); From 277df671e89b15926e038be60510aba3de9f1ff4 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Sun, 3 Mar 2024 18:39:22 -0500 Subject: [PATCH 11/22] unit test default endpoint --- opentelemetry-otlp/src/exporter/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 8c41174730..c022070972 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -315,6 +315,19 @@ mod tests { ); } + #[cfg(feature = "http-json")] + #[test] + fn test_default_http_json_endpoint() { + use crate::WithExportConfig; + + let exporter_builder = crate::new_exporter().http().with_protocol(crate::Protocol::HttpJson); + + assert_eq!( + exporter_builder.exporter_config.endpoint, + "http://localhost:4318" + ); + } + #[test] fn test_url_decode() { let test_cases = vec![ From 5d5e06eb0bb198ab05449398193e0a976d59656f Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Sun, 3 Mar 2024 19:08:35 -0500 Subject: [PATCH 12/22] test default protocol --- opentelemetry-otlp/src/exporter/mod.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index c022070972..1031e8b038 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -30,6 +30,7 @@ pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION #[cfg(all( feature = "trace", + feature = "http-json", not(feature = "http-proto"), not(feature = "grpc-tonic") ))] @@ -315,17 +316,25 @@ mod tests { ); } - #[cfg(feature = "http-json")] + #[cfg(all( + feature = "trace", + feature = "http-json", + not(feature = "http-proto"), + not(feature = "grpc-tonic") + ))] #[test] - fn test_default_http_json_endpoint() { - use crate::WithExportConfig; - - let exporter_builder = crate::new_exporter().http().with_protocol(crate::Protocol::HttpJson); + fn test_http_json_defaults() { + let exporter_builder = crate::new_exporter().http(); assert_eq!( exporter_builder.exporter_config.endpoint, "http://localhost:4318" ); + + assert_eq!( + exporter_builder.exporter_config.protocol, + crate::Protocol::HttpJson + ); } #[test] From f93058c197c223b7d00d6fb623a5eebcb7fe8e8b Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Sun, 17 Mar 2024 22:11:30 -0400 Subject: [PATCH 13/22] enable http-json for all signals --- opentelemetry-otlp/src/exporter/http/logs.rs | 24 +++++-- .../src/exporter/http/metrics.rs | 23 +++++-- opentelemetry-otlp/src/exporter/mod.rs | 62 +++++++++++-------- 3 files changed, 75 insertions(+), 34 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/http/logs.rs b/opentelemetry-otlp/src/exporter/http/logs.rs index 6bb8b76493..3fb4d43a43 100644 --- a/opentelemetry-otlp/src/exporter/http/logs.rs +++ b/opentelemetry-otlp/src/exporter/http/logs.rs @@ -52,18 +52,32 @@ impl LogExporter for OtlpHttpClient { } } -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { + use crate::exporter::default_protocol; + #[cfg(feature = "http-json")] + use crate::Protocol; use opentelemetry_proto::tonic::collector::logs::v1::ExportLogsServiceRequest; use prost::Message; let req = ExportLogsServiceRequest { resource_logs: logs.into_iter().map(Into::into).collect(), }; - let mut buf = vec![]; - req.encode(&mut buf).map_err(crate::Error::from)?; - - Ok((buf, "application/x-protobuf")) + let buf; + let ctype; + match default_protocol() { + #[cfg(feature = "http-json")] + Protocol::HttpJson => { + let json_struct = serde_json::to_string_pretty(&req).unwrap(); + buf = json_struct.into(); + ctype = "application/json"; + } + _ => { + buf = req.encode_to_vec(); + ctype = "application/x-protobuf"; + } + }; + Ok((buf, ctype)) } #[cfg(not(feature = "http-proto"))] diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index 6bcdb99f42..509b244b38 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -48,16 +48,31 @@ impl MetricsClient for OtlpHttpClient { } } -#[cfg(feature = "http-proto")] +#[cfg(any(feature = "http-proto", feature = "http-json"))] fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { + use crate::exporter::default_protocol; + #[cfg(feature = "http-json")] + use crate::Protocol; use prost::Message; let req: opentelemetry_proto::tonic::collector::metrics::v1::ExportMetricsServiceRequest = (&*metrics).into(); - let mut buf = vec![]; - req.encode(&mut buf).map_err(crate::Error::from)?; - Ok((buf, "application/x-protobuf")) + let buf; + let ctype; + match default_protocol() { + #[cfg(feature = "http-json")] + Protocol::HttpJson => { + let json_struct = serde_json::to_string_pretty(&req).unwrap(); + buf = json_struct.into(); + ctype = "application/json"; + } + _ => { + buf = req.encode_to_vec(); + ctype = "application/x-protobuf"; + } + }; + Ok((buf, ctype)) } #[cfg(not(feature = "http-proto"))] diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 1031e8b038..343878281b 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,26 +28,27 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; +/// Default protocol if no features are enabled. +pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = ""; + #[cfg(all( - feature = "trace", feature = "http-json", - not(feature = "http-proto"), - not(feature = "grpc-tonic") + not(all(feature = "grpc-tonic", feature = "http-proto")) ))] /// Default protocol, using http-json. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; -#[cfg(feature = "http-proto")] +#[cfg(all( + feature = "http-proto", + not(all(feature = "grpc-tonic", feature = "http-json")) +))] /// Default protocol, using http-proto. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; #[cfg(all( feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json")) ))] -/// Default protocol, using grpc as http-proto or http-json feature is not enabled. +/// Default protocol, using grpc pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; -#[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))))] -/// Default protocol if no features are enabled. -pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = ""; const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF: &str = "http/protobuf"; const OTEL_EXPORTER_OTLP_PROTOCOL_GRPC: &str = "grpc"; @@ -278,7 +279,6 @@ fn parse_header_key_value_string(key_value_string: &str) -> Option<(&str, String #[cfg(test)] #[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))] mod tests { - pub(crate) fn run_env_test(env_vars: T, f: F) where F: FnOnce(), @@ -316,25 +316,37 @@ mod tests { ); } - #[cfg(all( - feature = "trace", - feature = "http-json", - not(feature = "http-proto"), - not(feature = "grpc-tonic") - ))] #[test] - fn test_http_json_defaults() { - let exporter_builder = crate::new_exporter().http(); + fn test_default_protocol() { + #[cfg(all( + feature = "http-json", + not(all(feature = "grpc-tonic", feature = "http-proto")) + ))] + { + assert_eq!( + crate::exporter::default_protocol(), + crate::Protocol::HttpJson + ); + } - assert_eq!( - exporter_builder.exporter_config.endpoint, - "http://localhost:4318" - ); + #[cfg(all( + feature = "http-proto", + not(all(feature = "grpc-tonic", feature = "http-json")) + ))] + { + assert_eq!( + crate::exporter::default_protocol(), + crate::Protocol::HttpBinary + ); + } - assert_eq!( - exporter_builder.exporter_config.protocol, - crate::Protocol::HttpJson - ); + #[cfg(all( + feature = "grpc-tonic", + not(all(feature = "http-proto", feature = "http-json")) + ))] + { + assert_eq!(crate::exporter::default_protocol(), crate::Protocol::Grpc); + } } #[test] From 0f1a805c767bf43cad1b87d61b7cf033d66ef14a Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Sun, 17 Mar 2024 22:40:25 -0400 Subject: [PATCH 14/22] fix lint --- opentelemetry-otlp/src/exporter/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index 343878281b..f2de9b6da4 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,9 +28,6 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; -/// Default protocol if no features are enabled. -pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = ""; - #[cfg(all( feature = "http-json", not(all(feature = "grpc-tonic", feature = "http-proto")) @@ -50,6 +47,10 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCO /// Default protocol, using grpc pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; +#[cfg(not(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json")))] +/// Default protocol if no features are enabled. +pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = ""; + const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF: &str = "http/protobuf"; const OTEL_EXPORTER_OTLP_PROTOCOL_GRPC: &str = "grpc"; const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON: &str = "http/json"; From e50ac0ee82f292d2aee312f3b59d4dd192b582d5 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Tue, 19 Mar 2024 15:40:14 -0400 Subject: [PATCH 15/22] run precommit --- opentelemetry-otlp/src/exporter/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index f2de9b6da4..a7ef38a940 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -28,21 +28,18 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL"; /// Compression algorithm to use, defaults to none. pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION"; -#[cfg(all( - feature = "http-json", - not(all(feature = "grpc-tonic", feature = "http-proto")) -))] +#[cfg(feature = "http-json")] /// Default protocol, using http-json. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON; #[cfg(all( feature = "http-proto", - not(all(feature = "grpc-tonic", feature = "http-json")) + not(any(feature = "grpc-tonic", feature = "http-json")) ))] /// Default protocol, using http-proto. pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF; #[cfg(all( feature = "grpc-tonic", - not(all(feature = "http-proto", feature = "http-json")) + not(any(feature = "http-proto", feature = "http-json")) ))] /// Default protocol, using grpc pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC; From ad8322baea40f0fcb64853c8cb044ca50a10dec7 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Mon, 25 Mar 2024 12:20:59 -0400 Subject: [PATCH 16/22] add unit test --- opentelemetry-otlp/src/exporter/http/logs.rs | 19 +++--- .../src/exporter/http/metrics.rs | 20 +++--- opentelemetry-otlp/src/exporter/http/trace.rs | 67 +++++++++++++++++-- opentelemetry-otlp/src/exporter/mod.rs | 6 +- 4 files changed, 86 insertions(+), 26 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/http/logs.rs b/opentelemetry-otlp/src/exporter/http/logs.rs index 3fb4d43a43..4f9cee9ecd 100644 --- a/opentelemetry-otlp/src/exporter/http/logs.rs +++ b/opentelemetry-otlp/src/exporter/http/logs.rs @@ -67,20 +67,23 @@ fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { let ctype; match default_protocol() { #[cfg(feature = "http-json")] - Protocol::HttpJson => { - let json_struct = serde_json::to_string_pretty(&req).unwrap(); - buf = json_struct.into(); - ctype = "application/json"; - } + Protocol::HttpJson => match serde_json::to_string_pretty(&req) { + Ok(json) => { + buf = json.into(); + ctype = "application/json"; + Ok((buf, ctype)) + } + Err(e) => Err(LogError::from(e.to_string())), + }, _ => { buf = req.encode_to_vec(); ctype = "application/x-protobuf"; + Ok((buf, ctype)) } - }; - Ok((buf, ctype)) + } } -#[cfg(not(feature = "http-proto"))] +#[cfg(not(any(feature = "http-proto", feature = "http-json")))] fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { Err(LogsError::Other( "No http protocol configured. Enable one via `http-proto`".into(), diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index 509b244b38..4d6862cebf 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -62,20 +62,24 @@ fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> let ctype; match default_protocol() { #[cfg(feature = "http-json")] - Protocol::HttpJson => { - let json_struct = serde_json::to_string_pretty(&req).unwrap(); - buf = json_struct.into(); - ctype = "application/json"; - } + #[cfg(feature = "http-json")] + Protocol::HttpJson => match serde_json::to_string_pretty(&req) { + Ok(json) => { + buf = json.into(); + ctype = "application/json"; + Ok((buf, ctype)) + } + Err(e) => Err(MetricsError::Other(e.to_string())), + }, _ => { buf = req.encode_to_vec(); ctype = "application/x-protobuf"; + Ok((buf, ctype)) } - }; - Ok((buf, ctype)) + } } -#[cfg(not(feature = "http-proto"))] +#[cfg(not(any(feature = "http-proto", feature = "http-json")))] fn build_body(_metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { Err(MetricsError::Other( "No http protocol configured. Enable one via `http-proto`".into(), diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index 3052c41b11..b12f2fcef6 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -83,17 +83,20 @@ fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { let ctype; match default_protocol() { #[cfg(feature = "http-json")] - Protocol::HttpJson => { - let json_struct = serde_json::to_string_pretty(&req).unwrap(); - buf = json_struct.into(); - ctype = "application/json"; - } + Protocol::HttpJson => match serde_json::to_string_pretty(&req) { + Ok(json) => { + buf = json.into(); + ctype = "application/json"; + Ok((buf, ctype)) + } + Err(e) => Err(TraceError::from(e.to_string())), + }, _ => { buf = req.encode_to_vec(); ctype = "application/x-protobuf"; + Ok((buf, ctype)) } - }; - Ok((buf, ctype)) + } } #[cfg(not(any(feature = "http-proto", feature = "http-json")))] @@ -102,3 +105,53 @@ fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { "No http protocol configured. Enable one via `http-proto` or `http-json`".into(), )) } + +#[cfg(any(feature = "http-proto", feature = "http-json"))] +mod tests { + #[test] + fn test_build_body() { + use crate::exporter::http::trace::build_body; + use opentelemetry::trace::{ + SpanContext, SpanId, SpanKind, Status, TraceFlags, TraceId, TraceState, + }; + use opentelemetry_sdk::export::trace::SpanData; + use opentelemetry_sdk::trace::{SpanEvents, SpanLinks}; + use opentelemetry_sdk::Resource; + use std::{borrow::Cow, time::SystemTime}; + + let span_data = (0..5) + .map(|_| SpanData { + span_context: SpanContext::new( + TraceId::from_u128(12), + SpanId::from_u64(12), + TraceFlags::default(), + false, + TraceState::default(), + ), + parent_span_id: SpanId::from_u64(12), + span_kind: SpanKind::Client, + name: Default::default(), + start_time: SystemTime::now(), + end_time: SystemTime::now(), + attributes: Vec::new(), + dropped_attributes_count: 0, + events: SpanEvents::default(), + links: SpanLinks::default(), + status: Status::Unset, + resource: Cow::Owned(Resource::empty()), + instrumentation_lib: Default::default(), + }) + .collect::>(); + + let result = build_body(span_data).unwrap(); + match crate::exporter::default_protocol() { + #[cfg(feature = "http-json")] + crate::Protocol::HttpJson => { + assert_eq!(result.1, "application/json") + } + _ => { + assert_eq!(result.1, "application/x-protobuf") + } + }; + } +} diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index a7ef38a940..6be463ef9d 100644 --- a/opentelemetry-otlp/src/exporter/mod.rs +++ b/opentelemetry-otlp/src/exporter/mod.rs @@ -318,7 +318,7 @@ mod tests { fn test_default_protocol() { #[cfg(all( feature = "http-json", - not(all(feature = "grpc-tonic", feature = "http-proto")) + not(any(feature = "grpc-tonic", feature = "http-proto")) ))] { assert_eq!( @@ -329,7 +329,7 @@ mod tests { #[cfg(all( feature = "http-proto", - not(all(feature = "grpc-tonic", feature = "http-json")) + not(any(feature = "grpc-tonic", feature = "http-json")) ))] { assert_eq!( @@ -340,7 +340,7 @@ mod tests { #[cfg(all( feature = "grpc-tonic", - not(all(feature = "http-proto", feature = "http-json")) + not(any(feature = "http-proto", feature = "http-json")) ))] { assert_eq!(crate::exporter::default_protocol(), crate::Protocol::Grpc); From 63fb044685521375a7a55517ad480c67d68febb1 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Fri, 29 Mar 2024 11:22:37 -0400 Subject: [PATCH 17/22] update changelog --- opentelemetry-otlp/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opentelemetry-otlp/CHANGELOG.md b/opentelemetry-otlp/CHANGELOG.md index 28d519daa2..004f12f6b8 100644 --- a/opentelemetry-otlp/CHANGELOG.md +++ b/opentelemetry-otlp/CHANGELOG.md @@ -8,6 +8,10 @@ ### Added +- Aded `http/json` support for all signals ([#1585]) + +[#1585]: https://github.com/open-telemetry/opentelemetry-rust/pull/1585 + - Added `DeltaTemporalitySelector` ([#1568]) [#1568]: https://github.com/open-telemetry/opentelemetry-rust/pull/1568 From f9abf2401dfa039444b3179b995d1cd68e1f37bd Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Fri, 29 Mar 2024 17:00:14 -0400 Subject: [PATCH 18/22] address code review --- opentelemetry-otlp/src/exporter/http/logs.rs | 14 ++------------ opentelemetry-otlp/src/exporter/http/metrics.rs | 14 ++------------ opentelemetry-otlp/src/exporter/http/trace.rs | 14 ++------------ 3 files changed, 6 insertions(+), 36 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/http/logs.rs b/opentelemetry-otlp/src/exporter/http/logs.rs index 4f9cee9ecd..7d92a24fc5 100644 --- a/opentelemetry-otlp/src/exporter/http/logs.rs +++ b/opentelemetry-otlp/src/exporter/http/logs.rs @@ -63,23 +63,13 @@ fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { let req = ExportLogsServiceRequest { resource_logs: logs.into_iter().map(Into::into).collect(), }; - let buf; - let ctype; match default_protocol() { #[cfg(feature = "http-json")] Protocol::HttpJson => match serde_json::to_string_pretty(&req) { - Ok(json) => { - buf = json.into(); - ctype = "application/json"; - Ok((buf, ctype)) - } + Ok(json) => Ok((json.into(), "application/json")), Err(e) => Err(LogError::from(e.to_string())), }, - _ => { - buf = req.encode_to_vec(); - ctype = "application/x-protobuf"; - Ok((buf, ctype)) - } + _ => Ok((req.encode_to_vec(), "application/x-protobuf")), } } diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index 4d6862cebf..528481e4bc 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -58,24 +58,14 @@ fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> let req: opentelemetry_proto::tonic::collector::metrics::v1::ExportMetricsServiceRequest = (&*metrics).into(); - let buf; - let ctype; match default_protocol() { #[cfg(feature = "http-json")] #[cfg(feature = "http-json")] Protocol::HttpJson => match serde_json::to_string_pretty(&req) { - Ok(json) => { - buf = json.into(); - ctype = "application/json"; - Ok((buf, ctype)) - } + Ok(json) => Ok((json.into(), "application/json")), Err(e) => Err(MetricsError::Other(e.to_string())), }, - _ => { - buf = req.encode_to_vec(); - ctype = "application/x-protobuf"; - Ok((buf, ctype)) - } + _ => Ok((req.encode_to_vec(), "application/x-protobuf")), } } diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index b12f2fcef6..21de0881f9 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -79,23 +79,13 @@ fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { let req = ExportTraceServiceRequest { resource_spans: spans.into_iter().map(Into::into).collect(), }; - let buf; - let ctype; match default_protocol() { #[cfg(feature = "http-json")] Protocol::HttpJson => match serde_json::to_string_pretty(&req) { - Ok(json) => { - buf = json.into(); - ctype = "application/json"; - Ok((buf, ctype)) - } + Ok(json) => Ok((json.into(), "application/json")), Err(e) => Err(TraceError::from(e.to_string())), }, - _ => { - buf = req.encode_to_vec(); - ctype = "application/x-protobuf"; - Ok((buf, ctype)) - } + _ => Ok((req.encode_to_vec(), "application/x-protobuf")), } } From 250fab497aabe5ef3d13c2a7da7d2deeab1fc8c5 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Fri, 29 Mar 2024 17:13:04 -0400 Subject: [PATCH 19/22] remove duplicate line --- opentelemetry-otlp/src/exporter/http/metrics.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index 528481e4bc..7886913f4d 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -59,7 +59,6 @@ fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> (&*metrics).into(); match default_protocol() { - #[cfg(feature = "http-json")] #[cfg(feature = "http-json")] Protocol::HttpJson => match serde_json::to_string_pretty(&req) { Ok(json) => Ok((json.into(), "application/json")), From 67552f807800c8f85f47018d5f3a6cb490128d72 Mon Sep 17 00:00:00 2001 From: Ramji Date: Sun, 31 Mar 2024 20:24:15 -0400 Subject: [PATCH 20/22] Update opentelemetry-otlp/src/exporter/http/metrics.rs Co-authored-by: Zhongyang Wu --- opentelemetry-otlp/src/exporter/http/metrics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index 7886913f4d..d3c2a424fc 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -71,6 +71,6 @@ fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> #[cfg(not(any(feature = "http-proto", feature = "http-json")))] fn build_body(_metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { Err(MetricsError::Other( - "No http protocol configured. Enable one via `http-proto`".into(), + "No http protocol configured. Enable `http-proto` or `http-json`".into(), )) } From 4ed5158dfad4096e84dc27aed38ed768f1c5a6c9 Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Sat, 20 Apr 2024 18:02:08 -0400 Subject: [PATCH 21/22] move build_body to otlphttpclient --- opentelemetry-otlp/src/exporter/http/logs.rs | 30 +------ .../src/exporter/http/metrics.rs | 29 +------ opentelemetry-otlp/src/exporter/http/mod.rs | 82 ++++++++++++++++++- opentelemetry-otlp/src/exporter/http/trace.rs | 82 +------------------ 4 files changed, 82 insertions(+), 141 deletions(-) diff --git a/opentelemetry-otlp/src/exporter/http/logs.rs b/opentelemetry-otlp/src/exporter/http/logs.rs index 7d92a24fc5..1d8e177693 100644 --- a/opentelemetry-otlp/src/exporter/http/logs.rs +++ b/opentelemetry-otlp/src/exporter/http/logs.rs @@ -19,7 +19,7 @@ impl LogExporter for OtlpHttpClient { _ => Err(LogError::Other("exporter is already shut down".into())), })?; - let (body, content_type) = build_body(batch)?; + let (body, content_type) = self.build_logs_export_body(batch)?; let mut request = http::Request::builder() .method(Method::POST) .uri(&self.collector_endpoint) @@ -51,31 +51,3 @@ impl LogExporter for OtlpHttpClient { let _ = self.client.lock().map(|mut c| c.take()); } } - -#[cfg(any(feature = "http-proto", feature = "http-json"))] -fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { - use crate::exporter::default_protocol; - #[cfg(feature = "http-json")] - use crate::Protocol; - use opentelemetry_proto::tonic::collector::logs::v1::ExportLogsServiceRequest; - use prost::Message; - - let req = ExportLogsServiceRequest { - resource_logs: logs.into_iter().map(Into::into).collect(), - }; - match default_protocol() { - #[cfg(feature = "http-json")] - Protocol::HttpJson => match serde_json::to_string_pretty(&req) { - Ok(json) => Ok((json.into(), "application/json")), - Err(e) => Err(LogError::from(e.to_string())), - }, - _ => Ok((req.encode_to_vec(), "application/x-protobuf")), - } -} - -#[cfg(not(any(feature = "http-proto", feature = "http-json")))] -fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { - Err(LogsError::Other( - "No http protocol configured. Enable one via `http-proto`".into(), - )) -} diff --git a/opentelemetry-otlp/src/exporter/http/metrics.rs b/opentelemetry-otlp/src/exporter/http/metrics.rs index d3c2a424fc..8fcf1bc362 100644 --- a/opentelemetry-otlp/src/exporter/http/metrics.rs +++ b/opentelemetry-otlp/src/exporter/http/metrics.rs @@ -21,7 +21,7 @@ impl MetricsClient for OtlpHttpClient { _ => Err(MetricsError::Other("exporter is already shut down".into())), })?; - let (body, content_type) = build_body(metrics)?; + let (body, content_type) = self.build_metrics_export_body(metrics)?; let mut request = http::Request::builder() .method(Method::POST) .uri(&self.collector_endpoint) @@ -47,30 +47,3 @@ impl MetricsClient for OtlpHttpClient { Ok(()) } } - -#[cfg(any(feature = "http-proto", feature = "http-json"))] -fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { - use crate::exporter::default_protocol; - #[cfg(feature = "http-json")] - use crate::Protocol; - use prost::Message; - - let req: opentelemetry_proto::tonic::collector::metrics::v1::ExportMetricsServiceRequest = - (&*metrics).into(); - - match default_protocol() { - #[cfg(feature = "http-json")] - Protocol::HttpJson => match serde_json::to_string_pretty(&req) { - Ok(json) => Ok((json.into(), "application/json")), - Err(e) => Err(MetricsError::Other(e.to_string())), - }, - _ => Ok((req.encode_to_vec(), "application/x-protobuf")), - } -} - -#[cfg(not(any(feature = "http-proto", feature = "http-json")))] -fn build_body(_metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { - Err(MetricsError::Other( - "No http protocol configured. Enable `http-proto` or `http-json`".into(), - )) -} diff --git a/opentelemetry-otlp/src/exporter/http/mod.rs b/opentelemetry-otlp/src/exporter/http/mod.rs index b8659f9a62..4cfc002486 100644 --- a/opentelemetry-otlp/src/exporter/http/mod.rs +++ b/opentelemetry-otlp/src/exporter/http/mod.rs @@ -1,17 +1,23 @@ +use super::{default_headers, default_protocol, parse_header_string}; use crate::{ - ExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, + ExportConfig, Protocol, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT, }; use http::{HeaderName, HeaderValue, Uri}; use opentelemetry_http::HttpClient; +#[cfg(feature = "logs")] +use opentelemetry_sdk::export::logs::LogData; +#[cfg(feature = "trace")] +use opentelemetry_sdk::export::trace::SpanData; +#[cfg(feature = "metrics")] +use opentelemetry_sdk::metrics::data::ResourceMetrics; +use prost::Message; use std::collections::HashMap; use std::env; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; -use super::{default_headers, default_protocol, parse_header_string}; - #[cfg(feature = "metrics")] mod metrics; @@ -181,7 +187,13 @@ impl HttpExporterBuilder { add_header_from_string(&input, &mut headers); } - Ok(OtlpHttpClient::new(http_client, endpoint, headers, timeout)) + Ok(OtlpHttpClient::new( + http_client, + endpoint, + headers, + self.exporter_config.protocol, + timeout, + )) } /// Create a log exporter with the current configuration @@ -254,6 +266,7 @@ struct OtlpHttpClient { client: Mutex>>, collector_endpoint: Uri, headers: HashMap, + protocol: Protocol, _timeout: Duration, } @@ -263,15 +276,76 @@ impl OtlpHttpClient { client: Arc, collector_endpoint: Uri, headers: HashMap, + protocol: Protocol, timeout: Duration, ) -> Self { OtlpHttpClient { client: Mutex::new(Some(client)), collector_endpoint, headers, + protocol, _timeout: timeout, } } + + #[cfg(feature = "trace")] + fn build_trace_export_body( + &self, + spans: Vec, + ) -> opentelemetry::trace::TraceResult<(Vec, &'static str)> { + use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; + + let req = ExportTraceServiceRequest { + resource_spans: spans.into_iter().map(Into::into).collect(), + }; + match self.protocol { + #[cfg(feature = "http-json")] + Protocol::HttpJson => match serde_json::to_string_pretty(&req) { + Ok(json) => Ok((json.into(), "application/json")), + Err(e) => Err(opentelemetry::trace::TraceError::from(e.to_string())), + }, + _ => Ok((req.encode_to_vec(), "application/x-protobuf")), + } + } + + #[cfg(feature = "logs")] + fn build_logs_export_body( + &self, + logs: Vec, + ) -> opentelemetry::logs::LogResult<(Vec, &'static str)> { + use opentelemetry_proto::tonic::collector::logs::v1::ExportLogsServiceRequest; + + let req = ExportLogsServiceRequest { + resource_logs: logs.into_iter().map(Into::into).collect(), + }; + match self.protocol { + #[cfg(feature = "http-json")] + Protocol::HttpJson => match serde_json::to_string_pretty(&req) { + Ok(json) => Ok((json.into(), "application/json")), + Err(e) => Err(opentelemetry::logs::LogError::from(e.to_string())), + }, + _ => Ok((req.encode_to_vec(), "application/x-protobuf")), + } + } + + #[cfg(feature = "metrics")] + fn build_metrics_export_body( + &self, + metrics: &mut ResourceMetrics, + ) -> opentelemetry::metrics::Result<(Vec, &'static str)> { + use opentelemetry_proto::tonic::collector::metrics::v1::ExportMetricsServiceRequest; + + let req: ExportMetricsServiceRequest = (&*metrics).into(); + + match self.protocol { + #[cfg(feature = "http-json")] + Protocol::HttpJson => match serde_json::to_string_pretty(&req) { + Ok(json) => Ok((json.into(), "application/json")), + Err(e) => Err(opentelemetry::metrics::MetricsError::Other(e.to_string())), + }, + _ => Ok((req.encode_to_vec(), "application/x-protobuf")), + } + } } fn build_endpoint_uri(endpoint: &str, path: &str) -> Result { diff --git a/opentelemetry-otlp/src/exporter/http/trace.rs b/opentelemetry-otlp/src/exporter/http/trace.rs index 21de0881f9..8e272c93cf 100644 --- a/opentelemetry-otlp/src/exporter/http/trace.rs +++ b/opentelemetry-otlp/src/exporter/http/trace.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use futures_core::future::BoxFuture; use http::{header::CONTENT_TYPE, Method}; -use opentelemetry::trace::{TraceError, TraceResult}; +use opentelemetry::trace::TraceError; use opentelemetry_sdk::export::trace::{ExportResult, SpanData, SpanExporter}; use super::OtlpHttpClient; @@ -21,7 +21,7 @@ impl SpanExporter for OtlpHttpClient { Err(err) => return Box::pin(std::future::ready(Err(err))), }; - let (body, content_type) = match build_body(batch) { + let (body, content_type) = match self.build_trace_export_body(batch) { Ok(body) => body, Err(e) => return Box::pin(std::future::ready(Err(e))), }; @@ -67,81 +67,3 @@ impl SpanExporter for OtlpHttpClient { let _ = self.client.lock().map(|mut c| c.take()); } } - -#[cfg(any(feature = "http-proto", feature = "http-json"))] -fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { - use crate::exporter::default_protocol; - #[cfg(feature = "http-json")] - use crate::Protocol; - use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; - use prost::Message; - - let req = ExportTraceServiceRequest { - resource_spans: spans.into_iter().map(Into::into).collect(), - }; - match default_protocol() { - #[cfg(feature = "http-json")] - Protocol::HttpJson => match serde_json::to_string_pretty(&req) { - Ok(json) => Ok((json.into(), "application/json")), - Err(e) => Err(TraceError::from(e.to_string())), - }, - _ => Ok((req.encode_to_vec(), "application/x-protobuf")), - } -} - -#[cfg(not(any(feature = "http-proto", feature = "http-json")))] -fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { - Err(TraceError::Other( - "No http protocol configured. Enable one via `http-proto` or `http-json`".into(), - )) -} - -#[cfg(any(feature = "http-proto", feature = "http-json"))] -mod tests { - #[test] - fn test_build_body() { - use crate::exporter::http::trace::build_body; - use opentelemetry::trace::{ - SpanContext, SpanId, SpanKind, Status, TraceFlags, TraceId, TraceState, - }; - use opentelemetry_sdk::export::trace::SpanData; - use opentelemetry_sdk::trace::{SpanEvents, SpanLinks}; - use opentelemetry_sdk::Resource; - use std::{borrow::Cow, time::SystemTime}; - - let span_data = (0..5) - .map(|_| SpanData { - span_context: SpanContext::new( - TraceId::from_u128(12), - SpanId::from_u64(12), - TraceFlags::default(), - false, - TraceState::default(), - ), - parent_span_id: SpanId::from_u64(12), - span_kind: SpanKind::Client, - name: Default::default(), - start_time: SystemTime::now(), - end_time: SystemTime::now(), - attributes: Vec::new(), - dropped_attributes_count: 0, - events: SpanEvents::default(), - links: SpanLinks::default(), - status: Status::Unset, - resource: Cow::Owned(Resource::empty()), - instrumentation_lib: Default::default(), - }) - .collect::>(); - - let result = build_body(span_data).unwrap(); - match crate::exporter::default_protocol() { - #[cfg(feature = "http-json")] - crate::Protocol::HttpJson => { - assert_eq!(result.1, "application/json") - } - _ => { - assert_eq!(result.1, "application/x-protobuf") - } - }; - } -} From ebc3cc3178c5e15d1e51c1737fc88f4b9b0970aa Mon Sep 17 00:00:00 2001 From: Ramajeyam Gopalraj Date: Sat, 20 Apr 2024 18:42:49 -0400 Subject: [PATCH 22/22] allow builder to accept protocol --- opentelemetry-otlp/Cargo.toml | 4 ++-- opentelemetry-otlp/src/exporter/http/mod.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/opentelemetry-otlp/Cargo.toml b/opentelemetry-otlp/Cargo.toml index d1fcd2025f..29787ed95b 100644 --- a/opentelemetry-otlp/Cargo.toml +++ b/opentelemetry-otlp/Cargo.toml @@ -71,11 +71,11 @@ tls-roots = ["tls", "tonic/tls-roots"] # http binary http-proto = ["prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "http", "trace", "metrics"] +# http json +http-json = ["serde_json", "prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "opentelemetry-proto/with-serde", "http", "trace", "metrics"] reqwest-blocking-client = ["reqwest/blocking", "opentelemetry-http/reqwest"] reqwest-client = ["reqwest", "opentelemetry-http/reqwest"] reqwest-rustls = ["reqwest", "reqwest/rustls-tls-native-roots"] -# http json -http-json = ["serde_json", "prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "opentelemetry-proto/with-serde", "http", "trace", "metrics"] # test integration-testing = ["tonic", "prost", "tokio/full", "trace"] diff --git a/opentelemetry-otlp/src/exporter/http/mod.rs b/opentelemetry-otlp/src/exporter/http/mod.rs index 4cfc002486..884971e028 100644 --- a/opentelemetry-otlp/src/exporter/http/mod.rs +++ b/opentelemetry-otlp/src/exporter/http/mod.rs @@ -116,6 +116,12 @@ impl Default for HttpExporterBuilder { } impl HttpExporterBuilder { + /// Specify the OTLP protocol to be used by the exporter + pub fn with_protocol(mut self, protocol: Protocol) -> Self { + self.exporter_config.protocol = protocol; + self + } + /// Assign client implementation pub fn with_http_client(mut self, client: T) -> Self { self.http_config.client = Some(Arc::new(client));