#[cfg(feature = "grpc-sys")]
use crate::exporter::grpcio::GrpcioExporterBuilder;
#[cfg(feature = "http-proto")]
use crate::exporter::http::HttpExporterBuilder;
#[cfg(feature = "grpc-tonic")]
use crate::exporter::tonic::TonicExporterBuilder;
use crate::{Error, Protocol};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use std::time::Duration;
pub const OTEL_EXPORTER_OTLP_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_ENDPOINT";
pub const OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT: &str = OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT;
pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL";
pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION";
#[cfg(feature = "http-proto")]
pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF;
#[cfg(all(
any(feature = "grpc-tonic", feature = "grpcio"),
not(feature = "http-proto")
))]
pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC;
#[cfg(not(any(any(feature = "grpc-tonic", feature = "grpcio", feature = "http-proto"))))]
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";
pub const OTEL_EXPORTER_OTLP_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_TIMEOUT";
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 = "grpc-sys")]
pub(crate) mod grpcio;
#[cfg(feature = "http-proto")]
pub(crate) mod http;
#[cfg(feature = "grpc-tonic")]
pub(crate) mod tonic;
#[derive(Debug)]
pub struct ExportConfig {
pub endpoint: String,
pub protocol: Protocol,
pub timeout: Duration,
}
impl Default for ExportConfig {
fn default() -> Self {
let protocol = default_protocol();
ExportConfig {
endpoint: default_endpoint(protocol),
protocol,
timeout: Duration::from_secs(OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT),
}
}
}
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Compression {
Gzip,
}
impl Display for Compression {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Compression::Gzip => write!(f, "gzip"),
}
}
}
impl FromStr for Compression {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"gzip" => Ok(Compression::Gzip),
_ => Err(Error::UnsupportedCompressionAlgorithm(s.to_string())),
}
}
}
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,
_ => Protocol::HttpBinary,
}
}
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(),
}
}
fn default_headers() -> HashMap<String, String> {
let mut headers = HashMap::new();
headers.insert(
"User-Agent".to_string(),
format!("OTel OTLP Exporter Rust/{}", env!("CARGO_PKG_VERSION")),
);
headers
}
pub trait HasExportConfig {
fn export_config(&mut self) -> &mut ExportConfig;
}
#[cfg(feature = "grpc-tonic")]
impl HasExportConfig for TonicExporterBuilder {
fn export_config(&mut self) -> &mut ExportConfig {
&mut self.exporter_config
}
}
#[cfg(feature = "grpc-sys")]
impl HasExportConfig for GrpcioExporterBuilder {
fn export_config(&mut self) -> &mut ExportConfig {
&mut self.exporter_config
}
}
#[cfg(feature = "http-proto")]
impl HasExportConfig for HttpExporterBuilder {
fn export_config(&mut self) -> &mut ExportConfig {
&mut self.exporter_config
}
}
pub trait WithExportConfig {
fn with_endpoint<T: Into<String>>(self, endpoint: T) -> Self;
fn with_protocol(self, protocol: Protocol) -> Self;
fn with_timeout(self, timeout: Duration) -> Self;
fn with_env(self) -> Self;
fn with_export_config(self, export_config: ExportConfig) -> Self;
}
impl<B: HasExportConfig> WithExportConfig for B {
fn with_endpoint<T: Into<String>>(mut self, endpoint: T) -> Self {
self.export_config().endpoint = endpoint.into();
self
}
fn with_protocol(mut self, protocol: Protocol) -> Self {
self.export_config().protocol = protocol;
self
}
fn with_timeout(mut self, timeout: Duration) -> Self {
self.export_config().timeout = timeout;
self
}
fn with_env(mut self) -> Self {
let protocol = match std::env::var(OTEL_EXPORTER_OTLP_PROTOCOL)
.unwrap_or_else(|_| OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT.to_string())
.as_str()
{
OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF => Protocol::HttpBinary,
OTEL_EXPORTER_OTLP_PROTOCOL_GRPC => Protocol::Grpc,
_ => default_protocol(),
};
self.export_config().protocol = protocol;
let endpoint = match std::env::var(OTEL_EXPORTER_OTLP_ENDPOINT) {
Ok(val) => val,
Err(_) => default_endpoint(protocol),
};
self.export_config().endpoint = endpoint;
let timeout = match std::env::var(OTEL_EXPORTER_OTLP_TIMEOUT) {
Ok(val) => u64::from_str(&val).unwrap_or(OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT),
Err(_) => OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
};
self.export_config().timeout = Duration::from_secs(timeout);
self
}
fn with_export_config(mut self, exporter_config: ExportConfig) -> Self {
self.export_config().endpoint = exporter_config.endpoint;
self.export_config().protocol = exporter_config.protocol;
self.export_config().timeout = exporter_config.timeout;
self
}
}
#[cfg(test)]
#[cfg(feature = "grpc-tonic")]
mod tests {
const LOCK_POISONED_MESSAGE: &str = "one of the other pipeline builder from env tests failed";
use crate::exporter::{
default_endpoint, default_protocol, WithExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT,
OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
OTEL_EXPORTER_OTLP_PROTOCOL_GRPC, OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF,
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
};
use crate::{new_exporter, Compression, Protocol, OTEL_EXPORTER_OTLP_PROTOCOL};
use std::str::FromStr;
use std::sync::Mutex;
static ENV_LOCK: Mutex<usize> = Mutex::new(0);
#[test]
fn test_pipeline_builder_from_env_default_vars() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(
exporter_builder.exporter_config.protocol,
default_protocol()
);
assert_eq!(
exporter_builder.exporter_config.endpoint,
default_endpoint(default_protocol())
);
assert_eq!(
exporter_builder.exporter_config.timeout,
std::time::Duration::from_secs(OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT)
);
}
#[test]
fn test_pipeline_builder_from_env_endpoint() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
std::env::set_var(OTEL_EXPORTER_OTLP_ENDPOINT, "http://example.com");
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(
exporter_builder.exporter_config.endpoint,
"http://example.com"
);
std::env::remove_var(OTEL_EXPORTER_OTLP_ENDPOINT);
assert!(std::env::var(OTEL_EXPORTER_OTLP_ENDPOINT).is_err());
}
#[test]
fn test_pipeline_builder_from_env_protocol_http_protobuf() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
std::env::set_var(
OTEL_EXPORTER_OTLP_PROTOCOL,
OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF,
);
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(
exporter_builder.exporter_config.protocol,
Protocol::HttpBinary
);
assert_eq!(
exporter_builder.exporter_config.endpoint,
OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT
);
std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
assert!(std::env::var(OTEL_EXPORTER_OTLP_PROTOCOL).is_err());
}
#[test]
fn test_pipeline_builder_from_env_protocol_grpc() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
std::env::set_var(
OTEL_EXPORTER_OTLP_PROTOCOL,
OTEL_EXPORTER_OTLP_PROTOCOL_GRPC,
);
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(exporter_builder.exporter_config.protocol, Protocol::Grpc);
assert_eq!(
exporter_builder.exporter_config.endpoint,
OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT
);
std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
assert!(std::env::var(OTEL_EXPORTER_OTLP_PROTOCOL).is_err());
}
#[test]
fn test_pipeline_builder_from_env_bad_protocol() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
std::env::set_var(OTEL_EXPORTER_OTLP_PROTOCOL, "bad_protocol");
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(
exporter_builder.exporter_config.protocol,
default_protocol()
);
assert_eq!(
exporter_builder.exporter_config.endpoint,
default_endpoint(default_protocol())
);
std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
assert!(std::env::var(OTEL_EXPORTER_OTLP_PROTOCOL).is_err());
}
#[test]
fn test_pipeline_builder_from_env_timeout() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
std::env::set_var(OTEL_EXPORTER_OTLP_TIMEOUT, "60");
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(
exporter_builder.exporter_config.timeout,
std::time::Duration::from_secs(60)
);
std::env::remove_var(OTEL_EXPORTER_OTLP_TIMEOUT);
assert!(std::env::var(OTEL_EXPORTER_OTLP_TIMEOUT).is_err());
}
#[test]
fn test_pipeline_builder_from_env_bad_timeout() {
let _env_lock = ENV_LOCK.lock().expect(LOCK_POISONED_MESSAGE);
std::env::set_var(OTEL_EXPORTER_OTLP_TIMEOUT, "bad_timeout");
let exporter_builder = new_exporter().tonic().with_env();
assert_eq!(
exporter_builder.exporter_config.timeout,
std::time::Duration::from_secs(OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT)
);
std::env::remove_var(OTEL_EXPORTER_OTLP_TIMEOUT);
assert!(std::env::var(OTEL_EXPORTER_OTLP_TIMEOUT).is_err());
}
#[test]
fn test_compression_parse() {
assert_eq!(Compression::from_str("gzip").unwrap(), Compression::Gzip);
Compression::from_str("bad_compression").expect_err("bad compression");
}
#[test]
fn test_compression_to_str() {
assert_eq!(Compression::Gzip.to_string(), "gzip");
}
}