Skip to main content

iii_sdk/telemetry/
types.rs

1/// Magic prefixes for binary frames over WebSocket
2pub const PREFIX_TRACES: &[u8] = b"OTLP";
3pub const PREFIX_METRICS: &[u8] = b"MTRC";
4pub const PREFIX_LOGS: &[u8] = b"LOGS";
5
6/// Connection state for the shared WebSocket
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ConnectionState {
9    Disconnected,
10    Connecting,
11    Connected,
12    Reconnecting,
13    Failed,
14}
15
16/// Configuration for WebSocket reconnection behavior
17#[derive(Debug, Clone)]
18pub struct ReconnectionConfig {
19    pub initial_delay_ms: u64,
20    pub max_delay_ms: u64,
21    pub backoff_multiplier: f64,
22    pub jitter_factor: f64,
23    pub max_retries: Option<u64>, // None for infinite
24    /// Maximum messages preserved across reconnects. Messages beyond this limit
25    /// are dropped to prevent delivering stale data after a long disconnect.
26    /// This is intentionally smaller than `OtelConfig::channel_capacity` (the
27    /// in-flight buffer between exporters and the WebSocket loop).
28    pub max_pending_messages: usize,
29}
30
31impl Default for ReconnectionConfig {
32    fn default() -> Self {
33        Self {
34            initial_delay_ms: 1000,
35            max_delay_ms: 30000,
36            backoff_multiplier: 2.0,
37            jitter_factor: 0.3,
38            max_retries: None,
39            max_pending_messages: 1000,
40        }
41    }
42}
43
44impl ReconnectionConfig {
45    /// Returns initial_delay_ms, clamped to a minimum of 1ms to prevent division by zero.
46    pub fn effective_initial_delay_ms(&self) -> u64 {
47        self.initial_delay_ms.max(1)
48    }
49}
50
51/// Configuration for OpenTelemetry initialization
52#[derive(Debug, Clone, Default)]
53pub struct OtelConfig {
54    pub enabled: Option<bool>,
55    pub service_name: Option<String>,
56    pub service_version: Option<String>,
57    pub service_namespace: Option<String>,
58    pub service_instance_id: Option<String>,
59    pub engine_ws_url: Option<String>,
60    pub metrics_enabled: Option<bool>,
61    pub metrics_export_interval_ms: Option<u64>,
62    pub reconnection_config: Option<ReconnectionConfig>,
63    /// Timeout in milliseconds for the shutdown sequence (default: 10,000)
64    pub shutdown_timeout_ms: Option<u64>,
65    /// Capacity of the internal telemetry message channel (default: 10,000).
66    /// This controls the in-flight message buffer between exporters and the
67    /// WebSocket connection loop. Intentionally larger than
68    /// `ReconnectionConfig::max_pending_messages` to absorb bursts during
69    /// normal operation while limiting stale data across reconnects.
70    pub channel_capacity: Option<usize>,
71    /// Whether to enable the log exporter (default: true)
72    pub logs_enabled: Option<bool>,
73    /// Log processor flush delay in milliseconds. Defaults to 100ms when not set.
74    pub logs_flush_interval_ms: Option<u64>,
75    /// Maximum number of log records exported per batch. Defaults to 1 when not set.
76    pub logs_batch_size: Option<usize>,
77    /// Whether to auto-instrument outgoing HTTP calls.
78    /// When `Some(true)` (default), `execute_traced_request()` can be used to
79    /// create CLIENT spans for reqwest requests. Set `Some(false)` to opt out.
80    /// `None` is treated as `true`.
81    pub fetch_instrumentation_enabled: Option<bool>,
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_reconnection_config_defaults() {
90        let config = ReconnectionConfig::default();
91        assert_eq!(config.initial_delay_ms, 1000);
92        assert_eq!(config.max_delay_ms, 30000);
93        assert_eq!(config.backoff_multiplier, 2.0);
94        assert_eq!(config.jitter_factor, 0.3);
95        assert_eq!(config.max_retries, None);
96        assert_eq!(config.max_pending_messages, 1000);
97    }
98
99    #[test]
100    fn test_otel_config_defaults() {
101        let config = OtelConfig::default();
102        assert!(config.enabled.is_none());
103        assert!(config.service_name.is_none());
104        assert!(config.engine_ws_url.is_none());
105        assert!(config.metrics_enabled.is_none());
106        assert!(config.reconnection_config.is_none());
107    }
108
109    #[test]
110    fn test_otel_config_has_fetch_instrumentation_enabled() {
111        let config = OtelConfig::default();
112        assert!(config.fetch_instrumentation_enabled.is_none());
113
114        let config_disabled = OtelConfig {
115            fetch_instrumentation_enabled: Some(false),
116            ..Default::default()
117        };
118        assert_eq!(config_disabled.fetch_instrumentation_enabled, Some(false));
119    }
120
121    #[test]
122    fn test_reconnection_config_zero_delay_clamped() {
123        let config = ReconnectionConfig {
124            initial_delay_ms: 0,
125            ..Default::default()
126        };
127        assert_eq!(config.effective_initial_delay_ms(), 1);
128    }
129
130    #[test]
131    fn test_otel_config_logs_batch_defaults() {
132        let config = OtelConfig::default();
133        assert!(config.logs_flush_interval_ms.is_none());
134        assert!(config.logs_batch_size.is_none());
135    }
136
137    #[test]
138    fn test_otel_config_logs_batch_explicit() {
139        let config = OtelConfig {
140            logs_flush_interval_ms: Some(200),
141            logs_batch_size: Some(5),
142            ..Default::default()
143        };
144        assert_eq!(config.logs_flush_interval_ms, Some(200));
145        assert_eq!(config.logs_batch_size, Some(5));
146    }
147}