Skip to content

Commit f4b3606

Browse files
committed
OTLP Exporter build to return own Result
1 parent 8aa5b00 commit f4b3606

File tree

13 files changed

+101
-122
lines changed

13 files changed

+101
-122
lines changed

examples/tracing-jaeger/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ publish = false
88
[dependencies]
99
opentelemetry = { path = "../../opentelemetry" }
1010
opentelemetry_sdk = { path = "../../opentelemetry-sdk", features = ["rt-tokio"] }
11-
opentelemetry-otlp = { workspace = true, features = ["tonic"] }
11+
opentelemetry-otlp = { workspace = true, features = ["grpc-tonic"] }
1212
tokio = { workspace = true, features = ["full"] }

examples/tracing-jaeger/src/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use opentelemetry::{
33
trace::{TraceContextExt, Tracer},
44
KeyValue,
55
};
6-
use opentelemetry_sdk::trace::{SdkTracerProvider, TraceError};
6+
use opentelemetry_otlp::ExporterBuildError;
7+
use opentelemetry_sdk::trace::SdkTracerProvider;
78
use opentelemetry_sdk::Resource;
89

910
use std::error::Error;
1011

11-
fn init_tracer_provider() -> Result<opentelemetry_sdk::trace::SdkTracerProvider, TraceError> {
12+
fn init_tracer_provider() -> Result<opentelemetry_sdk::trace::SdkTracerProvider, ExporterBuildError>
13+
{
1214
let exporter = opentelemetry_otlp::SpanExporter::builder()
1315
.with_tonic()
1416
.build()?;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ impl LogExporter for OtlpHttpClient {
1515

1616
let (body, content_type) = self
1717
.build_logs_export_body(batch)
18-
.map_err(|e| OTelSdkError::InternalFailure(e.to_string()))?;
18+
.map_err(|e| OTelSdkError::InternalFailure(e))?;
1919

2020
let mut request = http::Request::builder()
2121
.method(Method::POST)

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

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{
2-
default_headers, default_protocol, parse_header_string,
2+
default_headers, default_protocol, parse_header_string, ExporterBuildError,
33
OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
44
};
55
use crate::{
@@ -108,7 +108,7 @@ impl HttpExporterBuilder {
108108
signal_endpoint_path: &str,
109109
signal_timeout_var: &str,
110110
signal_http_headers_var: &str,
111-
) -> Result<OtlpHttpClient, crate::Error> {
111+
) -> Result<OtlpHttpClient, ExporterBuildError> {
112112
let endpoint = resolve_http_endpoint(
113113
signal_endpoint_var,
114114
signal_endpoint_path,
@@ -168,12 +168,12 @@ impl HttpExporterBuilder {
168168
.unwrap_or_else(|_| reqwest::blocking::Client::new())
169169
})
170170
.join()
171-
.unwrap(), // Unwrap thread result
171+
.unwrap(), // TODO: Return ExporterBuildError::ThreadSpawnFailed
172172
) as Arc<dyn HttpClient>);
173173
}
174174
}
175175

176-
let http_client = http_client.ok_or(crate::Error::NoHttpClient)?;
176+
let http_client = http_client.ok_or(ExporterBuildError::NoHttpClient)?;
177177

178178
#[allow(clippy::mutable_key_type)] // http headers are not mutated
179179
let mut headers: HashMap<HeaderName, HeaderValue> = self
@@ -208,9 +208,7 @@ impl HttpExporterBuilder {
208208

209209
/// Create a log exporter with the current configuration
210210
#[cfg(feature = "trace")]
211-
pub fn build_span_exporter(
212-
mut self,
213-
) -> Result<crate::SpanExporter, opentelemetry_sdk::trace::TraceError> {
211+
pub fn build_span_exporter(mut self) -> Result<crate::SpanExporter, ExporterBuildError> {
214212
use crate::{
215213
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_HEADERS,
216214
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
@@ -228,7 +226,7 @@ impl HttpExporterBuilder {
228226

229227
/// Create a log exporter with the current configuration
230228
#[cfg(feature = "logs")]
231-
pub fn build_log_exporter(mut self) -> opentelemetry_sdk::logs::LogResult<crate::LogExporter> {
229+
pub fn build_log_exporter(mut self) -> Result<crate::LogExporter, ExporterBuildError> {
232230
use crate::{
233231
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_HEADERS,
234232
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
@@ -249,7 +247,7 @@ impl HttpExporterBuilder {
249247
pub fn build_metrics_exporter(
250248
mut self,
251249
temporality: opentelemetry_sdk::metrics::Temporality,
252-
) -> opentelemetry_sdk::metrics::MetricResult<crate::MetricExporter> {
250+
) -> Result<crate::MetricExporter, ExporterBuildError> {
253251
use crate::{
254252
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_HEADERS,
255253
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
@@ -309,7 +307,7 @@ impl OtlpHttpClient {
309307
match self.protocol {
310308
#[cfg(feature = "http-json")]
311309
Protocol::HttpJson => match serde_json::to_string_pretty(&req) {
312-
Ok(json) => Ok((json.into(), "application/json")),
310+
Ok(json) => Ok((json.into_bytes(), "application/json")),
313311
Err(e) => Err(opentelemetry_sdk::trace::TraceError::from(e.to_string())),
314312
},
315313
_ => Ok((req.encode_to_vec(), "application/x-protobuf")),
@@ -320,7 +318,7 @@ impl OtlpHttpClient {
320318
fn build_logs_export_body(
321319
&self,
322320
logs: LogBatch<'_>,
323-
) -> opentelemetry_sdk::logs::LogResult<(Vec<u8>, &'static str)> {
321+
) -> Result<(Vec<u8>, &'static str), String> {
324322
use opentelemetry_proto::tonic::collector::logs::v1::ExportLogsServiceRequest;
325323
let resource_logs = group_logs_by_resource_and_scope(logs, &self.resource);
326324
let req = ExportLogsServiceRequest { resource_logs };
@@ -329,7 +327,7 @@ impl OtlpHttpClient {
329327
#[cfg(feature = "http-json")]
330328
Protocol::HttpJson => match serde_json::to_string_pretty(&req) {
331329
Ok(json) => Ok((json.into(), "application/json")),
332-
Err(e) => Err(opentelemetry_sdk::logs::LogError::from(e.to_string())),
330+
Err(e) => Err(e.to_string()),
333331
},
334332
_ => Ok((req.encode_to_vec(), "application/x-protobuf")),
335333
}
@@ -357,21 +355,24 @@ impl OtlpHttpClient {
357355
}
358356
}
359357

360-
fn build_endpoint_uri(endpoint: &str, path: &str) -> Result<Uri, crate::Error> {
358+
fn build_endpoint_uri(endpoint: &str, path: &str) -> Result<Uri, ExporterBuildError> {
361359
let path = if endpoint.ends_with('/') && path.starts_with('/') {
362360
path.strip_prefix('/').unwrap()
363361
} else {
364362
path
365363
};
366-
format!("{endpoint}{path}").parse().map_err(From::from)
364+
let endpoint = format!("{endpoint}{path}");
365+
endpoint.parse().map_err(|er: http::uri::InvalidUri| {
366+
ExporterBuildError::InvalidUri(endpoint, er.to_string())
367+
})
367368
}
368369

369370
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#endpoint-urls-for-otlphttp
370371
fn resolve_http_endpoint(
371372
signal_endpoint_var: &str,
372373
signal_endpoint_path: &str,
373374
provided_endpoint: Option<String>,
374-
) -> Result<Uri, crate::Error> {
375+
) -> Result<Uri, ExporterBuildError> {
375376
// per signal env var is not modified
376377
if let Some(endpoint) = env::var(signal_endpoint_var)
377378
.ok()
@@ -388,14 +389,18 @@ fn resolve_http_endpoint(
388389
return Ok(endpoint);
389390
}
390391

391-
provided_endpoint
392-
.map(|e| e.parse().map_err(From::from))
393-
.unwrap_or_else(|| {
394-
build_endpoint_uri(
395-
OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
396-
signal_endpoint_path,
397-
)
398-
})
392+
if let Some(provider_endpoint) = provided_endpoint {
393+
provider_endpoint
394+
.parse()
395+
.map_err(|er: http::uri::InvalidUri| {
396+
ExporterBuildError::InvalidUri(provider_endpoint, er.to_string())
397+
})
398+
} else {
399+
build_endpoint_uri(
400+
OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
401+
signal_endpoint_path,
402+
)
403+
}
399404
}
400405

401406
#[allow(clippy::mutable_key_type)] // http headers are not mutated

opentelemetry-otlp/src/exporter/mod.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
use crate::exporter::http::HttpExporterBuilder;
77
#[cfg(feature = "grpc-tonic")]
88
use crate::exporter::tonic::TonicExporterBuilder;
9-
use crate::{Error, Protocol};
9+
use crate::Protocol;
1010
#[cfg(feature = "serialize")]
1111
use serde::{Deserialize, Serialize};
1212
use std::fmt::{Display, Formatter};
1313
use std::str::FromStr;
1414
use std::time::Duration;
15+
use thiserror::Error;
1516

1617
/// Target to which the exporter is going to send signals, defaults to https://localhost:4317.
1718
/// Learn about the relationship between this constant and metrics/spans/logs at
@@ -92,6 +93,41 @@ impl Default for ExportConfig {
9293
}
9394
}
9495

96+
#[derive(Error, Debug)]
97+
/// Errors that can occur while building an exporter.
98+
// TODO: Refine and polish this.
99+
pub enum ExporterBuildError {
100+
/// Spawning a new thread failed.
101+
#[error("Spawning a new thread failed. Unable to create Reqwest-Blocking client.")]
102+
ThreadSpawnFailed,
103+
104+
/// Feature required to use the specified compression algorithm.
105+
#[cfg(any(not(feature = "gzip-tonic"), not(feature = "zstd-tonic")))]
106+
#[error("feature '{0}' is required to use the compression algorithm '{1}'")]
107+
FeatureRequiredForCompressionAlgorithm(&'static str, Compression),
108+
109+
/// No Http client specified.
110+
#[error("no http client specified")]
111+
NoHttpClient,
112+
113+
/// Unsupported compression algorithm.
114+
#[error("unsupported compression algorithm '{0}'")]
115+
UnsupportedCompressionAlgorithm(String),
116+
117+
/// Invalid URI.
118+
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
119+
#[error("invalid URI {0}. Reason {1}")]
120+
InvalidUri(String, String),
121+
122+
/// Failed due to an internal error.
123+
/// The error message is intended for logging purposes only and should not
124+
/// be used to make programmatic decisions. It is implementation-specific
125+
/// and subject to change without notice. Consumers of this error should not
126+
/// rely on its content beyond logging.
127+
#[error("Reason: {0}")]
128+
InternalFailure(String),
129+
}
130+
95131
/// The compression algorithm to use when sending data.
96132
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
97133
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -112,13 +148,15 @@ impl Display for Compression {
112148
}
113149

114150
impl FromStr for Compression {
115-
type Err = Error;
151+
type Err = ExporterBuildError;
116152

117153
fn from_str(s: &str) -> Result<Self, Self::Err> {
118154
match s {
119155
"gzip" => Ok(Compression::Gzip),
120156
"zstd" => Ok(Compression::Zstd),
121-
_ => Err(Error::UnsupportedCompressionAlgorithm(s.to_string())),
157+
_ => Err(ExporterBuildError::UnsupportedCompressionAlgorithm(
158+
s.to_string(),
159+
)),
122160
}
123161
}
124162
}

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

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use tonic::transport::Channel;
1212
#[cfg(feature = "tls")]
1313
use tonic::transport::ClientTlsConfig;
1414

15+
use super::ExporterBuildError;
1516
use super::{default_headers, parse_header_string, OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT};
1617
use crate::exporter::Compression;
1718
use crate::{
@@ -46,21 +47,21 @@ pub struct TonicConfig {
4647
}
4748

4849
impl TryFrom<Compression> for tonic::codec::CompressionEncoding {
49-
type Error = crate::Error;
50+
type Error = ExporterBuildError;
5051

51-
fn try_from(value: Compression) -> Result<Self, Self::Error> {
52+
fn try_from(value: Compression) -> Result<Self, ExporterBuildError> {
5253
match value {
5354
#[cfg(feature = "gzip-tonic")]
5455
Compression::Gzip => Ok(tonic::codec::CompressionEncoding::Gzip),
5556
#[cfg(not(feature = "gzip-tonic"))]
56-
Compression::Gzip => Err(crate::Error::FeatureRequiredForCompressionAlgorithm(
57+
Compression::Gzip => Err(ExporterBuildError::FeatureRequiredForCompressionAlgorithm(
5758
"gzip-tonic",
5859
Compression::Gzip,
5960
)),
6061
#[cfg(feature = "zstd-tonic")]
6162
Compression::Zstd => Ok(tonic::codec::CompressionEncoding::Zstd),
6263
#[cfg(not(feature = "zstd-tonic"))]
63-
Compression::Zstd => Err(crate::Error::FeatureRequiredForCompressionAlgorithm(
64+
Compression::Zstd => Err(ExporterBuildError::FeatureRequiredForCompressionAlgorithm(
6465
"zstd-tonic",
6566
Compression::Zstd,
6667
)),
@@ -151,7 +152,7 @@ impl TonicExporterBuilder {
151152
signal_timeout_var: &str,
152153
signal_compression_var: &str,
153154
signal_headers_var: &str,
154-
) -> Result<(Channel, BoxInterceptor, Option<CompressionEncoding>), crate::Error> {
155+
) -> Result<(Channel, BoxInterceptor, Option<CompressionEncoding>), ExporterBuildError> {
155156
let compression = self.resolve_compression(signal_compression_var)?;
156157

157158
let (headers_from_env, headers_for_logging) = parse_headers_from_env(signal_headers_var);
@@ -194,7 +195,8 @@ impl TonicExporterBuilder {
194195
// Used for logging the endpoint
195196
let endpoint_clone = endpoint.clone();
196197

197-
let endpoint = Channel::from_shared(endpoint).map_err(crate::Error::from)?;
198+
let endpoint = Channel::from_shared(endpoint)
199+
.map_err(|op| ExporterBuildError::InvalidUri(endpoint_clone.clone(), op.to_string()))?;
198200
let timeout = match env::var(signal_timeout_var)
199201
.ok()
200202
.or(env::var(OTEL_EXPORTER_OTLP_TIMEOUT).ok())
@@ -210,7 +212,7 @@ impl TonicExporterBuilder {
210212
let channel = match self.tonic_config.tls_config {
211213
Some(tls_config) => endpoint
212214
.tls_config(tls_config)
213-
.map_err(crate::Error::from)?,
215+
.map_err(|er| ExporterBuildError::InternalFailure(er.to_string()))?,
214216
None => endpoint,
215217
}
216218
.timeout(timeout)
@@ -243,7 +245,7 @@ impl TonicExporterBuilder {
243245
fn resolve_compression(
244246
&self,
245247
env_override: &str,
246-
) -> Result<Option<CompressionEncoding>, crate::Error> {
248+
) -> Result<Option<CompressionEncoding>, ExporterBuildError> {
247249
if let Some(compression) = self.tonic_config.compression {
248250
Ok(Some(compression.try_into()?))
249251
} else if let Ok(compression) = env::var(env_override) {
@@ -257,9 +259,7 @@ impl TonicExporterBuilder {
257259

258260
/// Build a new tonic log exporter
259261
#[cfg(feature = "logs")]
260-
pub(crate) fn build_log_exporter(
261-
self,
262-
) -> Result<crate::logs::LogExporter, opentelemetry_sdk::logs::LogError> {
262+
pub(crate) fn build_log_exporter(self) -> Result<crate::logs::LogExporter, ExporterBuildError> {
263263
use crate::exporter::tonic::logs::TonicLogsClient;
264264

265265
otel_debug!(name: "LogsTonicChannelBuilding");
@@ -281,7 +281,7 @@ impl TonicExporterBuilder {
281281
pub(crate) fn build_metrics_exporter(
282282
self,
283283
temporality: opentelemetry_sdk::metrics::Temporality,
284-
) -> opentelemetry_sdk::metrics::MetricResult<crate::MetricExporter> {
284+
) -> Result<crate::MetricExporter, ExporterBuildError> {
285285
use crate::MetricExporter;
286286
use metrics::TonicMetricsClient;
287287

@@ -301,9 +301,7 @@ impl TonicExporterBuilder {
301301

302302
/// Build a new tonic span exporter
303303
#[cfg(feature = "trace")]
304-
pub(crate) fn build_span_exporter(
305-
self,
306-
) -> Result<crate::SpanExporter, opentelemetry_sdk::trace::TraceError> {
304+
pub(crate) fn build_span_exporter(self) -> Result<crate::SpanExporter, ExporterBuildError> {
307305
use crate::exporter::tonic::trace::TonicTracesClient;
308306

309307
otel_debug!(name: "TracesTonicChannelBuilding");

opentelemetry-otlp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ mod span;
223223

224224
pub use crate::exporter::Compression;
225225
pub use crate::exporter::ExportConfig;
226+
pub use crate::exporter::ExporterBuildError;
226227
#[cfg(feature = "trace")]
227228
#[cfg(any(feature = "http-proto", feature = "http-json", feature = "grpc-tonic"))]
228229
pub use crate::span::{

opentelemetry-otlp/src/logs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::fmt::Debug;
88

99
use opentelemetry_sdk::{error::OTelSdkResult, logs::LogBatch};
1010

11-
use crate::{HasExportConfig, NoExporterBuilderSet};
11+
use crate::{ExporterBuildError, HasExportConfig, NoExporterBuilderSet};
1212

1313
#[cfg(feature = "grpc-tonic")]
1414
use crate::{HasTonicConfig, TonicExporterBuilder, TonicExporterBuilderSet};
@@ -61,7 +61,7 @@ impl LogExporterBuilder<NoExporterBuilderSet> {
6161

6262
#[cfg(feature = "grpc-tonic")]
6363
impl LogExporterBuilder<TonicExporterBuilderSet> {
64-
pub fn build(self) -> Result<LogExporter, opentelemetry_sdk::logs::LogError> {
64+
pub fn build(self) -> Result<LogExporter, ExporterBuildError> {
6565
let result = self.client.0.build_log_exporter();
6666
otel_debug!(name: "LogExporterBuilt", result = format!("{:?}", &result));
6767
result
@@ -70,7 +70,7 @@ impl LogExporterBuilder<TonicExporterBuilderSet> {
7070

7171
#[cfg(any(feature = "http-proto", feature = "http-json"))]
7272
impl LogExporterBuilder<HttpExporterBuilderSet> {
73-
pub fn build(self) -> Result<LogExporter, opentelemetry_sdk::logs::LogError> {
73+
pub fn build(self) -> Result<LogExporter, ExporterBuildError> {
7474
self.client.0.build_log_exporter()
7575
}
7676
}

0 commit comments

Comments
 (0)