use std::time::Duration;
#[derive(Clone, Debug, Default)]
pub enum OtelExporter {
#[default]
OtlpGrpc,
OtlpHttp,
Jaeger,
Zipkin,
Console,
None,
}
#[derive(Clone, Debug, Default)]
pub enum TraceSampler {
#[default]
AlwaysOn,
AlwaysOff,
TraceIdRatio(f64),
ParentBased,
}
#[derive(Clone, Debug)]
pub struct OtelConfig {
pub service_name: String,
pub service_version: Option<String>,
pub service_namespace: Option<String>,
pub deployment_environment: Option<String>,
pub endpoint: Option<String>,
pub exporter: OtelExporter,
pub sampler: TraceSampler,
pub export_timeout: Duration,
pub export_interval: Duration,
pub max_queue_size: usize,
pub max_export_batch_size: usize,
pub enable_metrics: bool,
pub propagate_context: bool,
pub resource_attributes: Vec<(String, String)>,
pub trace_headers: Vec<String>,
pub exclude_paths: Vec<String>,
}
impl Default for OtelConfig {
fn default() -> Self {
Self {
service_name: "rustapi-service".to_string(),
service_version: None,
service_namespace: None,
deployment_environment: None,
endpoint: None,
exporter: OtelExporter::default(),
sampler: TraceSampler::default(),
export_timeout: Duration::from_secs(30),
export_interval: Duration::from_secs(5),
max_queue_size: 2048,
max_export_batch_size: 512,
enable_metrics: true,
propagate_context: true,
resource_attributes: Vec::new(),
trace_headers: vec![
"user-agent".to_string(),
"content-type".to_string(),
"x-request-id".to_string(),
],
exclude_paths: vec!["/health".to_string(), "/metrics".to_string()],
}
}
}
impl OtelConfig {
pub fn builder() -> OtelConfigBuilder {
OtelConfigBuilder::default()
}
}
#[derive(Default)]
pub struct OtelConfigBuilder {
config: OtelConfig,
}
impl OtelConfigBuilder {
pub fn service_name(mut self, name: impl Into<String>) -> Self {
self.config.service_name = name.into();
self
}
pub fn service_version(mut self, version: impl Into<String>) -> Self {
self.config.service_version = Some(version.into());
self
}
pub fn service_namespace(mut self, namespace: impl Into<String>) -> Self {
self.config.service_namespace = Some(namespace.into());
self
}
pub fn deployment_environment(mut self, env: impl Into<String>) -> Self {
self.config.deployment_environment = Some(env.into());
self
}
pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
self.config.endpoint = Some(endpoint.into());
self
}
pub fn exporter(mut self, exporter: OtelExporter) -> Self {
self.config.exporter = exporter;
self
}
pub fn sampler(mut self, sampler: TraceSampler) -> Self {
self.config.sampler = sampler;
self
}
pub fn export_timeout(mut self, timeout: Duration) -> Self {
self.config.export_timeout = timeout;
self
}
pub fn export_interval(mut self, interval: Duration) -> Self {
self.config.export_interval = interval;
self
}
pub fn max_queue_size(mut self, size: usize) -> Self {
self.config.max_queue_size = size;
self
}
pub fn max_export_batch_size(mut self, size: usize) -> Self {
self.config.max_export_batch_size = size;
self
}
pub fn enable_metrics(mut self, enabled: bool) -> Self {
self.config.enable_metrics = enabled;
self
}
pub fn propagate_context(mut self, enabled: bool) -> Self {
self.config.propagate_context = enabled;
self
}
pub fn resource_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.config
.resource_attributes
.push((key.into(), value.into()));
self
}
pub fn resource_attributes(mut self, attrs: Vec<(String, String)>) -> Self {
self.config.resource_attributes.extend(attrs);
self
}
pub fn trace_header(mut self, header: impl Into<String>) -> Self {
self.config.trace_headers.push(header.into());
self
}
pub fn trace_headers(mut self, headers: Vec<String>) -> Self {
self.config.trace_headers.extend(headers);
self
}
pub fn exclude_path(mut self, path: impl Into<String>) -> Self {
self.config.exclude_paths.push(path.into());
self
}
pub fn exclude_paths(mut self, paths: Vec<String>) -> Self {
self.config.exclude_paths.extend(paths);
self
}
pub fn build(self) -> OtelConfig {
self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = OtelConfig::default();
assert_eq!(config.service_name, "rustapi-service");
assert!(config.propagate_context);
assert!(config.enable_metrics);
}
#[test]
fn test_builder() {
let config = OtelConfig::builder()
.service_name("my-service")
.service_version("1.0.0")
.endpoint("http://localhost:4317")
.exporter(OtelExporter::OtlpGrpc)
.sampler(TraceSampler::TraceIdRatio(0.5))
.resource_attribute("env", "production")
.exclude_path("/ready")
.build();
assert_eq!(config.service_name, "my-service");
assert_eq!(config.service_version, Some("1.0.0".to_string()));
assert_eq!(config.endpoint, Some("http://localhost:4317".to_string()));
assert_eq!(config.resource_attributes.len(), 1);
assert!(config.exclude_paths.contains(&"/ready".to_string()));
}
#[test]
fn test_sampler_default() {
let sampler = TraceSampler::default();
matches!(sampler, TraceSampler::AlwaysOn);
}
}