init_tracing_opentelemetry/otlp/
mod.rs

1#[cfg(feature = "metrics")]
2pub mod metrics;
3pub mod traces;
4
5#[cfg(feature = "metrics")]
6use opentelemetry::metrics::MeterProvider;
7#[cfg(feature = "metrics")]
8use opentelemetry_sdk::metrics::SdkMeterProvider;
9
10use opentelemetry::trace::TracerProvider;
11use opentelemetry_sdk::trace::SdkTracerProvider;
12
13#[must_use = "Recommend holding with 'let _guard = ' pattern to ensure final traces/metrics are sent to the server"]
14/// On Drop of the `OtelGuard` instance,
15/// the wrapped Tracer/Meter Provider is force to flush and to shutdown (ignoring error).
16pub struct OtelGuard {
17    #[cfg(feature = "metrics")]
18    pub(crate) meter_provider: SdkMeterProvider,
19    pub(crate) tracer_provider: SdkTracerProvider,
20}
21
22impl OtelGuard {
23    #[must_use]
24    pub fn tracer_provider(&self) -> &impl TracerProvider {
25        &self.tracer_provider
26    }
27
28    #[cfg(feature = "metrics")]
29    #[must_use]
30    pub fn meter_provider(&self) -> &impl MeterProvider {
31        &self.meter_provider
32    }
33}
34
35impl Drop for OtelGuard {
36    #[allow(unused_must_use)]
37    fn drop(&mut self) {
38        let _ = self.tracer_provider.force_flush();
39        let _ = self.tracer_provider.shutdown();
40        #[cfg(feature = "metrics")]
41        {
42            let _ = self.meter_provider.force_flush();
43            let _ = self.meter_provider.shutdown();
44        }
45    }
46}
47
48#[allow(unused_mut)]
49pub(crate) fn infer_protocol(
50    maybe_protocol: Option<&str>,
51    maybe_endpoint: Option<&str>,
52) -> Option<String> {
53    let mut maybe_protocol = match (maybe_protocol, maybe_endpoint) {
54        (Some(protocol), _) => Some(protocol.to_string()),
55        (None, Some(endpoint)) => {
56            if endpoint.contains(":4317") {
57                Some("grpc".to_string())
58            } else {
59                Some("http/protobuf".to_string())
60            }
61        }
62        _ => None,
63    };
64    #[cfg(feature = "tls")]
65    if maybe_protocol.as_deref() == Some("grpc")
66        && maybe_endpoint.is_some_and(|e| e.starts_with("https"))
67    {
68        maybe_protocol = Some("grpc/tls".to_string());
69    }
70
71    maybe_protocol
72}
73
74#[cfg(test)]
75mod tests {
76    use assert2::assert;
77    use rstest::rstest;
78
79    use super::*;
80
81    #[rstest]
82    #[case(None, None, None)] //Devskim: ignore DS137138
83    #[case(Some("http/protobuf"), None, Some("http/protobuf"))] //Devskim: ignore DS137138
84    #[case(Some("grpc"), None, Some("grpc"))] //Devskim: ignore DS137138
85    #[case(None, Some("http://localhost:4317"), Some("grpc"))] //Devskim: ignore DS137138
86    #[cfg_attr(
87        feature = "tls",
88        case(None, Some("https://localhost:4317"), Some("grpc/tls"))
89    )]
90    #[cfg_attr(
91        feature = "tls",
92        case(Some("grpc/tls"), Some("https://localhost:4317"), Some("grpc/tls"))
93    )]
94    #[case(
95        Some("http/protobuf"),
96        Some("http://localhost:4318/v1/traces"), //Devskim: ignore DS137138
97        Some("http/protobuf"),
98    )]
99    #[case(
100        Some("http/protobuf"),
101        Some("https://examples.com:4318/v1/traces"),
102        Some("http/protobuf")
103    )]
104    #[case(
105        Some("http/protobuf"),
106        Some("https://examples.com:4317"),
107        Some("http/protobuf")
108    )]
109    fn test_infer_protocol(
110        #[case] traces_protocol: Option<&str>,
111        #[case] traces_endpoint: Option<&str>,
112        #[case] expected_protocol: Option<&str>,
113    ) {
114        assert!(infer_protocol(traces_protocol, traces_endpoint).as_deref() == expected_protocol);
115    }
116}