use crate::exporter::ExporterType;
use opentelemetry::global;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::trace::TracerProvider;
use opentelemetry_sdk::Resource;
use std::error::Error;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct TracingConfig {
pub service_name: String,
pub exporter_type: ExporterType,
pub jaeger_endpoint: Option<String>,
pub otlp_endpoint: Option<String>,
pub sampling_rate: f64,
pub environment: String,
pub service_version: Option<String>,
}
impl Default for TracingConfig {
fn default() -> Self {
Self {
service_name: "mockforge".to_string(),
exporter_type: ExporterType::Jaeger,
jaeger_endpoint: Some("http://localhost:14268/api/traces".to_string()),
otlp_endpoint: None,
sampling_rate: 1.0,
environment: "development".to_string(),
service_version: None,
}
}
}
impl TracingConfig {
pub fn with_jaeger(service_name: String, endpoint: String) -> Self {
Self {
service_name,
exporter_type: ExporterType::Jaeger,
jaeger_endpoint: Some(endpoint),
otlp_endpoint: None,
..Default::default()
}
}
pub fn with_otlp(service_name: String, endpoint: String) -> Self {
Self {
service_name,
exporter_type: ExporterType::Otlp,
jaeger_endpoint: None,
otlp_endpoint: Some(endpoint),
..Default::default()
}
}
pub fn with_sampling_rate(mut self, rate: f64) -> Self {
self.sampling_rate = rate;
self
}
pub fn with_environment(mut self, env: String) -> Self {
self.environment = env;
self
}
pub fn with_service_version(mut self, version: String) -> Self {
self.service_version = Some(version);
self
}
}
pub fn init_tracer(
config: TracingConfig,
) -> Result<global::BoxedTracer, Box<dyn Error + Send + Sync>> {
match config.exporter_type {
ExporterType::Jaeger => init_jaeger_tracer(config),
ExporterType::Otlp => init_otlp_tracer(config),
}
}
fn init_jaeger_tracer(
config: TracingConfig,
) -> Result<global::BoxedTracer, Box<dyn Error + Send + Sync>> {
let endpoint = config.jaeger_endpoint.ok_or("Jaeger endpoint not configured")?;
#[allow(deprecated)]
let _tracer_provider = opentelemetry_jaeger::new_agent_pipeline()
.with_service_name(&config.service_name)
.with_endpoint(&endpoint)
.with_trace_config(opentelemetry_sdk::trace::Config::default().with_sampler(
opentelemetry_sdk::trace::Sampler::TraceIdRatioBased(config.sampling_rate),
))
.install_simple()?;
let tracer = global::tracer("mockforge");
Ok(tracer)
}
fn init_otlp_tracer(
config: TracingConfig,
) -> Result<global::BoxedTracer, Box<dyn Error + Send + Sync>> {
let endpoint = config.otlp_endpoint.ok_or("OTLP endpoint not configured")?;
let resource = Resource::default();
let mut exporter_builder = opentelemetry_otlp::TonicExporterBuilder::default();
exporter_builder = exporter_builder.with_endpoint(endpoint);
exporter_builder = exporter_builder.with_timeout(Duration::from_secs(10));
let exporter = exporter_builder.build_span_exporter()?;
let tracer_provider = TracerProvider::builder()
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.with_config(
opentelemetry_sdk::trace::Config::default()
.with_resource(resource)
.with_sampler(opentelemetry_sdk::trace::Sampler::TraceIdRatioBased(
config.sampling_rate,
)),
)
.build();
global::set_tracer_provider(tracer_provider.clone());
let tracer = global::tracer("mockforge");
Ok(tracer)
}
pub fn shutdown_tracer() {
global::shutdown_tracer_provider();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = TracingConfig::default();
assert_eq!(config.service_name, "mockforge");
assert_eq!(config.sampling_rate, 1.0);
assert_eq!(config.environment, "development");
assert_eq!(config.exporter_type, ExporterType::Jaeger);
assert!(config.jaeger_endpoint.is_some());
assert!(config.otlp_endpoint.is_none());
}
#[test]
fn test_jaeger_config() {
let config = TracingConfig::with_jaeger(
"test-service".to_string(),
"http://custom:14268/api/traces".to_string(),
)
.with_sampling_rate(0.5)
.with_environment("staging".to_string())
.with_service_version("1.0.0".to_string());
assert_eq!(config.service_name, "test-service");
assert_eq!(config.exporter_type, ExporterType::Jaeger);
assert_eq!(config.jaeger_endpoint, Some("http://custom:14268/api/traces".to_string()));
assert_eq!(config.sampling_rate, 0.5);
assert_eq!(config.environment, "staging");
assert_eq!(config.service_version, Some("1.0.0".to_string()));
}
#[test]
fn test_otlp_config() {
let config = TracingConfig::with_otlp(
"test-service".to_string(),
"http://otel-collector:4317".to_string(),
)
.with_sampling_rate(0.8)
.with_environment("production".to_string())
.with_service_version("2.0.0".to_string());
assert_eq!(config.service_name, "test-service");
assert_eq!(config.exporter_type, ExporterType::Otlp);
assert_eq!(config.otlp_endpoint, Some("http://otel-collector:4317".to_string()));
assert!(config.jaeger_endpoint.is_none());
assert_eq!(config.sampling_rate, 0.8);
assert_eq!(config.environment, "production");
assert_eq!(config.service_version, Some("2.0.0".to_string()));
}
}