diff --git a/opentelemetry/src/exporter/trace/mod.rs b/opentelemetry/src/exporter/trace/mod.rs index 06f4d02967..2ff8076f95 100644 --- a/opentelemetry/src/exporter/trace/mod.rs +++ b/opentelemetry/src/exporter/trace/mod.rs @@ -7,11 +7,12 @@ use crate::{ use async_trait::async_trait; #[cfg(feature = "http")] use http::Request; +use serde::export::Formatter; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; #[cfg(all(feature = "http", feature = "reqwest"))] use std::convert::TryInto; -use std::fmt::Debug; +use std::fmt::{Debug, Display}; use std::sync::Arc; use std::time::SystemTime; @@ -28,10 +29,6 @@ pub trait ExportError: std::error::Error + Send + Sync + 'static { } } -#[cfg(all(feature = "reqwest", feature = "http"))] -#[async_trait] -impl ExportError for reqwest::Error {} - /// `SpanExporter` defines the interface that protocol-specific exporters must /// implement so that they can be plugged into OpenTelemetry SDK and support /// sending of telemetry data. @@ -81,6 +78,63 @@ pub trait HttpClient: Debug + Send + Sync { async fn send(&self, request: Request>) -> ExportResult; } +/// Error when sending http requests visa HttpClient implementation +#[cfg(feature = "http")] +#[cfg_attr(docsrs, doc(cfg(feature = "http")))] +#[derive(Debug)] +pub enum HttpClientError { + /// Errors from reqwest + #[cfg(all(feature = "reqwest", feature = "http"))] + ReqwestError(reqwest::Error), + + /// Errors from surf + #[cfg(all(feature = "surf", feature = "http"))] + SurfError(surf::Error), + + /// Other errors + Other(String), +} + +impl std::error::Error for HttpClientError {} + +#[cfg(feature = "http")] +#[cfg_attr(docsrs, doc(cfg(feature = "http")))] +impl ExportError for HttpClientError {} + +#[cfg(feature = "http")] +#[cfg_attr(docsrs, doc(cfg(feature = "http")))] +impl Display for HttpClientError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + #[cfg(all(feature = "reqwest", feature = "http"))] + HttpClientError::ReqwestError(err) => { + write!(f, "error when sending requests using reqwest, {}", err) + } + #[cfg(all(feature = "surf", feature = "http"))] + HttpClientError::SurfError(err) => write!( + f, + "error when sending requests using surf, {}", + err.to_string() + ), + HttpClientError::Other(msg) => write!(f, "{}", msg), + } + } +} + +#[cfg(all(feature = "reqwest", feature = "http"))] +impl From for HttpClientError { + fn from(err: reqwest::Error) -> Self { + HttpClientError::ReqwestError(err) + } +} + +#[cfg(all(feature = "surf", feature = "http"))] +impl From for HttpClientError { + fn from(err: surf::Error) -> Self { + HttpClientError::SurfError(err) + } +} + /// `SpanData` contains all the information collected by a `Span` and can be used /// by exporters as a standard input. #[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))] @@ -120,9 +174,15 @@ pub struct SpanData { impl HttpClient for reqwest::Client { async fn send(&self, request: Request>) -> ExportResult { let _result = self - .execute(request.try_into()?) - .await? - .error_for_status()?; + .execute( + request + .try_into() + .map_err::(Into::into)?, + ) + .await + .map_err::(Into::into)? + .error_for_status() + .map_err::(Into::into)?; Ok(()) } } @@ -131,7 +191,15 @@ impl HttpClient for reqwest::Client { #[async_trait] impl HttpClient for reqwest::blocking::Client { async fn send(&self, request: Request>) -> ExportResult { - let _result = self.execute(request.try_into()?)?.error_for_status()?; + let _result = self + .execute( + request + .try_into() + .map_err::(Into::into)?, + ) + .map_err::(Into::into)? + .error_for_status() + .map_err::(Into::into)?; Ok(()) } } @@ -141,17 +209,28 @@ impl HttpClient for reqwest::blocking::Client { impl HttpClient for surf::Client { async fn send(&self, request: Request>) -> ExportResult { let (parts, body) = request.into_parts(); - let uri = parts.uri.to_string().parse()?; + let uri = parts + .uri + .to_string() + .parse() + .map_err(|err: surf::http::url::ParseError| HttpClientError::Other(err.to_string()))?; let req = surf::Request::builder(surf::http::Method::Post, uri) .content_type("application/json") .body(body); - let result = self.send(req).await?; + let result = self + .send(req) + .await + .map_err::(Into::into)?; if result.status().is_success() { Ok(()) } else { - Err(result.status().canonical_reason().into()) + Err(HttpClientError::SurfError(surf::Error::from_str( + result.status(), + result.status().canonical_reason(), + )) + .into()) } } } diff --git a/opentelemetry/src/exporter/trace/stdout.rs b/opentelemetry/src/exporter/trace/stdout.rs index 27fa0e6e07..d89dfbe80d 100644 --- a/opentelemetry/src/exporter/trace/stdout.rs +++ b/opentelemetry/src/exporter/trace/stdout.rs @@ -134,11 +134,11 @@ where if self.pretty_print { self.writer .write_all(format!("{:#?}\n", span).as_bytes()) - .map_err(|err| Error::from(err))?; + .map_err::(Into::into)?; } else { self.writer .write_all(format!("{:?}\n", span).as_bytes()) - .map_err(|err| Error::from(err))?; + .map_err::(Into::into)?; } } diff --git a/opentelemetry/src/sdk/trace/span_processor.rs b/opentelemetry/src/sdk/trace/span_processor.rs index 0655266aba..89f9208192 100644 --- a/opentelemetry/src/sdk/trace/span_processor.rs +++ b/opentelemetry/src/sdk/trace/span_processor.rs @@ -296,7 +296,7 @@ impl BatchSpanProcessor { ); } let send_result = ch.send(results); - if let Err(_) = send_result { + if send_result.is_err() { global::handle_error(TraceError::Other("fail to send the export response from worker handle in BatchProcessor".to_string())) } } @@ -336,7 +336,7 @@ impl BatchSpanProcessor { } exporter.shutdown(); let send_result = ch.send(results); - if let Err(_) = send_result { + if send_result.is_err() { global::handle_error(TraceError::Other("fail to send the export response from worker handle in BatchProcessor".to_string())) } break;