Skip to main content

rs_zero/observability/
otlp.rs

1use std::{collections::BTreeMap, time::Duration};
2
3use crate::observability::{ObservabilityError, ObservabilityResult};
4
5/// OTLP transport protocol.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum OtlpProtocol {
8    /// gRPC OTLP.
9    Grpc,
10    /// HTTP/protobuf OTLP.
11    HttpProtobuf,
12}
13
14/// OTLP traces exporter configuration.
15#[derive(Debug, Clone, PartialEq)]
16pub struct OtlpTraceConfig {
17    /// Collector endpoint.
18    pub endpoint: String,
19    /// Transport protocol.
20    pub protocol: OtlpProtocol,
21    /// Request headers.
22    pub headers: BTreeMap<String, String>,
23    /// Resource attributes.
24    pub resource: BTreeMap<String, String>,
25    /// Sampling ratio in `[0.0, 1.0]`.
26    pub sampling_ratio: f64,
27    /// Export timeout.
28    pub timeout: Duration,
29}
30
31impl Default for OtlpTraceConfig {
32    fn default() -> Self {
33        Self {
34            endpoint: "http://127.0.0.1:4317".to_string(),
35            protocol: OtlpProtocol::Grpc,
36            headers: BTreeMap::new(),
37            resource: BTreeMap::new(),
38            sampling_ratio: 1.0,
39            timeout: Duration::from_secs(5),
40        }
41    }
42}
43
44/// Handle used to flush and shut down tracing exporters.
45#[derive(Debug, Clone, Default)]
46pub struct TraceShutdownHandle {
47    installed: bool,
48}
49
50impl TraceShutdownHandle {
51    /// Creates a handle representing an installed exporter.
52    pub fn installed() -> Self {
53        Self { installed: true }
54    }
55
56    /// Returns whether this handle owns an installed exporter.
57    pub fn is_installed(&self) -> bool {
58        self.installed
59    }
60
61    /// Flushes pending spans. The default build has no external collector.
62    pub fn flush(&self) -> ObservabilityResult<()> {
63        Ok(())
64    }
65
66    /// Shuts down the exporter. The default build has no external collector.
67    pub fn shutdown(self) -> ObservabilityResult<()> {
68        Ok(())
69    }
70}
71
72/// Validates and normalizes OTLP trace config.
73pub fn build_otlp_trace_config(config: OtlpTraceConfig) -> ObservabilityResult<OtlpTraceConfig> {
74    if config.endpoint.trim().is_empty() {
75        return Err(ObservabilityError::MissingOtlpEndpoint);
76    }
77    Ok(config)
78}
79
80#[cfg(test)]
81mod tests {
82    use super::{OtlpTraceConfig, TraceShutdownHandle, build_otlp_trace_config};
83    use crate::observability::ObservabilityError;
84
85    #[test]
86    fn otlp_config_requires_endpoint() {
87        let error = build_otlp_trace_config(OtlpTraceConfig {
88            endpoint: String::new(),
89            ..OtlpTraceConfig::default()
90        })
91        .expect_err("endpoint");
92        assert_eq!(error, ObservabilityError::MissingOtlpEndpoint);
93    }
94
95    #[test]
96    fn shutdown_handle_flushes_without_collector() {
97        let handle = TraceShutdownHandle::installed();
98        assert!(handle.is_installed());
99        handle.flush().expect("flush");
100        handle.shutdown().expect("shutdown");
101    }
102}