use opentelemetry_sdk::trace::TraceError;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ExporterConfig {
pub endpoint: String,
pub timeout: Duration,
pub use_tls: bool,
pub headers: Vec<(String, String)>,
}
impl Default for ExporterConfig {
fn default() -> Self {
Self {
endpoint: "http://localhost:4317".to_string(),
timeout: Duration::from_secs(10),
use_tls: false,
headers: Vec::new(),
}
}
}
pub enum TraceExporter {
#[cfg(feature = "otlp")]
Otlp,
Console,
Noop,
}
impl TraceExporter {
#[cfg(feature = "otlp")]
pub fn otlp(_config: ExporterConfig) -> Self {
Self::Otlp
}
pub fn console() -> Self {
Self::Console
}
pub fn noop() -> Self {
Self::Noop
}
pub async fn build(
self,
service_name: &str,
) -> Result<opentelemetry_sdk::trace::Tracer, TraceError> {
use opentelemetry::KeyValue;
use opentelemetry::trace::TracerProvider;
use opentelemetry_sdk::Resource;
let resource = Resource::builder()
.with_service_name(service_name.to_string())
.with_attribute(KeyValue::new("service.version", env!("CARGO_PKG_VERSION")))
.build();
match self {
#[cfg(feature = "otlp")]
Self::Otlp => {
use opentelemetry_otlp::WithExportConfig;
opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint("http://localhost:4317")
.build()
.map_err(|e| TraceError::Other(Box::new(e)))
.and_then(|exporter| {
let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_batch_exporter(exporter)
.with_resource(resource)
.build();
Ok(provider.tracer(service_name.to_string()))
})
}
Self::Console => {
let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_resource(resource)
.build();
Ok(provider.tracer(service_name.to_string()))
}
Self::Noop => {
let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_resource(resource)
.build();
Ok(provider.tracer(service_name.to_string()))
}
}
}
}
pub struct ExporterBuilder {
config: ExporterConfig,
}
impl ExporterBuilder {
pub fn new() -> Self {
Self {
config: ExporterConfig::default(),
}
}
pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
self.config.endpoint = endpoint.into();
self
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.config.timeout = timeout;
self
}
pub fn with_tls(mut self, use_tls: bool) -> Self {
self.config.use_tls = use_tls;
self
}
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.config.headers.push((key.into(), value.into()));
self
}
#[cfg(feature = "otlp")]
pub fn build_otlp(self) -> TraceExporter {
TraceExporter::otlp(self.config)
}
pub fn build_console(self) -> TraceExporter {
TraceExporter::console()
}
}
impl Default for ExporterBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exporter_config() {
let config = ExporterConfig {
endpoint: "http://localhost:4318".to_string(),
timeout: Duration::from_secs(30),
use_tls: true,
..Default::default()
};
assert_eq!(config.endpoint, "http://localhost:4318");
assert_eq!(config.timeout, Duration::from_secs(30));
assert!(config.use_tls);
}
#[test]
fn test_exporter_builder() {
let exporter = ExporterBuilder::new()
.with_endpoint("http://custom:4317")
.with_timeout(Duration::from_secs(20))
.with_tls(true)
.with_header("Authorization", "Bearer token")
.build_console();
match exporter {
TraceExporter::Console => {
}
_ => panic!("Expected console exporter"),
}
}
}