Skip to main content

otel_init/
guard.rs

1use anyhow::Result;
2use opentelemetry_sdk::{
3    logs::SdkLoggerProvider, metrics::SdkMeterProvider, trace::SdkTracerProvider,
4};
5use tracing::warn;
6
7/// A guard that holds the tracer provider, meter provider, and logger provider and ensures proper cleanup.
8///
9/// The guard has unique ownership of provider shutdown and cannot be cloned.
10///
11/// ```compile_fail
12/// use otel_init::OtelGuard;
13///
14/// let guard = OtelGuard::new(None, None, None);
15/// let _duplicate = guard.clone();
16/// ```
17#[derive(Debug)]
18pub struct OtelGuard {
19    tracer_provider: Option<SdkTracerProvider>,
20    meter_provider: Option<SdkMeterProvider>,
21    logger_provider: Option<SdkLoggerProvider>,
22}
23
24impl OtelGuard {
25    /// Create a new guard with the given providers
26    pub fn new(
27        tracer_provider: Option<SdkTracerProvider>,
28        meter_provider: Option<SdkMeterProvider>,
29        logger_provider: Option<SdkLoggerProvider>,
30    ) -> Self {
31        Self {
32            tracer_provider,
33            meter_provider,
34            logger_provider,
35        }
36    }
37
38    // Set the tracer provider
39    pub fn with_tracer_provider(mut self, tracer_provider: SdkTracerProvider) -> Self {
40        self.tracer_provider = Some(tracer_provider);
41        self
42    }
43
44    // Set the meter provider
45    pub fn with_meter_provider(mut self, meter_provider: SdkMeterProvider) -> Self {
46        self.meter_provider = Some(meter_provider);
47        self
48    }
49
50    // Set the logger provider
51    pub fn with_logger_provider(mut self, logger_provider: SdkLoggerProvider) -> Self {
52        self.logger_provider = Some(logger_provider);
53        self
54    }
55
56    /// Manually shutdown all providers
57    ///
58    /// This method attempts to shut down all providers, even if some fail.
59    pub fn shutdown(mut self) -> Result<()> {
60        let mut errors = Vec::new();
61        if let Some(tracer_provider) = self.tracer_provider.take()
62            && let Err(err) = tracer_provider.shutdown()
63        {
64            errors.push(err);
65        }
66
67        if let Some(meter_provider) = self.meter_provider.take()
68            && let Err(err) = meter_provider.shutdown()
69        {
70            errors.push(err);
71        }
72
73        if let Some(logger_provider) = self.logger_provider.take()
74            && let Err(err) = logger_provider.shutdown()
75        {
76            errors.push(err);
77        }
78
79        match errors.is_empty() {
80            true => Ok(()),
81            false => Err(anyhow::anyhow!(
82                "Failed to shutdown some providers: {errors:?}"
83            )),
84        }
85    }
86}
87
88// Drop the guard and shutdown the providers
89impl Drop for OtelGuard {
90    fn drop(&mut self) {
91        if let Some(tracer_provider) = self.tracer_provider.take()
92            && let Err(err) = tracer_provider.shutdown()
93        {
94            warn!("Failed to shutdown tracer provider: {err:?}");
95        }
96        if let Some(meter_provider) = self.meter_provider.take()
97            && let Err(err) = meter_provider.shutdown()
98        {
99            warn!("Failed to shutdown meter provider: {err:?}");
100        }
101        if let Some(logger_provider) = self.logger_provider.take()
102            && let Err(err) = logger_provider.shutdown()
103        {
104            warn!("Failed to shutdown logger provider: {err:?}");
105        }
106    }
107}