diff --git a/opentelemetry-otlp/CHANGELOG.md b/opentelemetry-otlp/CHANGELOG.md index c22a6dfdd7..dfbd03926c 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]) - Add `webkpi-roots` features to `reqwest` and `tonic` backends diff --git a/opentelemetry-otlp/Cargo.toml b/opentelemetry-otlp/Cargo.toml index fde442be0d..956012b95b 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"] @@ -71,6 +72,8 @@ tls-webkpi-roots = ["tls", "tonic/tls-webpki-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", "opentelemetry-http/reqwest-rustls"] 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/logs.rs b/opentelemetry-otlp/src/exporter/http/logs.rs index 6bb8b76493..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,24 +51,3 @@ impl LogExporter for OtlpHttpClient { let _ = self.client.lock().map(|mut c| c.take()); } } - -#[cfg(feature = "http-proto")] -fn build_body(logs: Vec) -> LogResult<(Vec, &'static str)> { - 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")) -} - -#[cfg(not(feature = "http-proto"))] -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 b5ec92eb63..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,22 +47,3 @@ impl MetricsClient for OtlpHttpClient { Ok(()) } } - -#[cfg(feature = "http-proto")] -fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec, &'static str)> { - 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")) -} - -#[cfg(not(feature = "http-proto"))] -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/mod.rs b/opentelemetry-otlp/src/exporter/http/mod.rs index a8b4e3d0c3..884971e028 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, 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, parse_header_string}; - #[cfg(feature = "metrics")] mod metrics; @@ -22,7 +28,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( @@ -98,7 +104,7 @@ impl Default for HttpExporterBuilder { fn default() -> Self { HttpExporterBuilder { exporter_config: ExportConfig { - protocol: Protocol::HttpBinary, + protocol: default_protocol(), ..ExportConfig::default() }, http_config: HttpConfig { @@ -110,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)); @@ -181,7 +193,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 +272,7 @@ struct OtlpHttpClient { client: Mutex>>, collector_endpoint: Uri, headers: HashMap, + protocol: Protocol, _timeout: Duration, } @@ -263,15 +282,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 e824978b57..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,24 +67,3 @@ impl SpanExporter for OtlpHttpClient { let _ = self.client.lock().map(|mut c| c.take()); } } - -#[cfg(feature = "http-proto")] -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")) -} - -#[cfg(not(feature = "http-proto"))] -fn build_body(spans: Vec) -> TraceResult<(Vec, &'static str)> { - Err(TraceError::Other( - "No http protocol configured. Enable one via `http-proto`".into(), - )) -} diff --git a/opentelemetry-otlp/src/exporter/mod.rs b/opentelemetry-otlp/src/exporter/mod.rs index e0beda5319..6be463ef9d 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; @@ -28,18 +28,29 @@ 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(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(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(feature = "http-proto")))] -/// Default protocol, using grpc as http-proto feature is not enabled. +#[cfg(all( + feature = "grpc-tonic", + 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; -#[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto"))))] + +#[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"; /// 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 +61,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 +123,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 +133,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 +161,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 +223,15 @@ 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(',') + .map(str::trim) + .filter_map(parse_header_key_value_string) +} + +#[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(); @@ -240,15 +261,7 @@ fn url_decode(value: &str) -> Option { } } -#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))] -fn parse_header_string(value: &str) -> impl Iterator { - value - .split_terminator(',') - .map(str::trim) - .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, String)> { key_value_string .split_once('=') @@ -262,9 +275,8 @@ fn parse_header_key_value_string(key_value_string: &str) -> Option<(&str, String } #[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) where F: FnOnce(), @@ -280,7 +292,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(); @@ -302,6 +314,39 @@ mod tests { ); } + #[test] + fn test_default_protocol() { + #[cfg(all( + feature = "http-json", + not(any(feature = "grpc-tonic", feature = "http-proto")) + ))] + { + assert_eq!( + crate::exporter::default_protocol(), + crate::Protocol::HttpJson + ); + } + + #[cfg(all( + feature = "http-proto", + not(any(feature = "grpc-tonic", feature = "http-json")) + ))] + { + assert_eq!( + crate::exporter::default_protocol(), + crate::Protocol::HttpBinary + ); + } + + #[cfg(all( + feature = "grpc-tonic", + not(any(feature = "http-proto", feature = "http-json")) + ))] + { + assert_eq!(crate::exporter::default_protocol(), crate::Protocol::Grpc); + } + } + #[test] fn test_url_decode() { let test_cases = vec![ diff --git a/opentelemetry-otlp/src/lib.rs b/opentelemetry-otlp/src/lib.rs index 2fa46310b4..82f5c421a1 100644 --- a/opentelemetry-otlp/src/lib.rs +++ b/opentelemetry-otlp/src/lib.rs @@ -102,6 +102,7 @@ //! The following feature flags offer additional configurations on http: //! //! * `http-proto`: Use http as transport layer, protobuf as body format. +//! * `http-json`: Use http as transport layer, JSON as body format. //! * `reqwest-blocking-client`: Use reqwest blocking http client. //! * `reqwest-client`: Use reqwest http client. //! * `reqwest-rustls`: Use reqwest with TLS with system trust roots via `rustls-native-certs` crate. @@ -249,7 +250,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")] @@ -280,7 +281,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() } @@ -316,7 +317,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), @@ -331,29 +332,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), @@ -395,10 +399,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 2c66b1f72f..3c0484d82b 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}; @@ -185,7 +185,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), } @@ -195,7 +195,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(), } } @@ -208,7 +208,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)