rocketmq_observability/
environment.rs1use std::ffi::OsStr;
16
17use crate::ObservabilityError;
18use crate::TelemetryBootstrapConfig;
19
20pub const OTEL_EXPORTER_OTLP_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_ENDPOINT";
21pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL";
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum StandardOtlpEnvironmentStatus {
25 Unchanged,
26 Applied,
27}
28
29pub fn apply_standard_otlp_environment(
40 config: &mut TelemetryBootstrapConfig,
41) -> Result<StandardOtlpEnvironmentStatus, ObservabilityError> {
42 let endpoint = crate::resolver::read_telemetry_environment_value(OTEL_EXPORTER_OTLP_ENDPOINT);
43 let protocol = crate::resolver::read_telemetry_environment_value(OTEL_EXPORTER_OTLP_PROTOCOL);
44 apply_standard_otlp_environment_values(config, endpoint.as_deref(), protocol.as_deref())
45}
46
47pub fn apply_standard_otlp_environment_values(
57 config: &mut TelemetryBootstrapConfig,
58 endpoint: Option<&OsStr>,
59 protocol: Option<&OsStr>,
60) -> Result<StandardOtlpEnvironmentStatus, ObservabilityError> {
61 crate::resolver::apply_standard_otlp_environment_values(config, endpoint, protocol)
62}
63
64#[cfg(test)]
65mod tests {
66 use std::ffi::OsStr;
67 #[cfg(any(unix, windows))]
68 use std::ffi::OsString;
69
70 use super::*;
71 use crate::LogsExporter;
72 use crate::MetricsExporter;
73 use crate::OtlpProtocol;
74 use crate::TraceExporter;
75
76 #[test]
77 fn missing_or_blank_endpoint_preserves_existing_configuration() {
78 let mut config = configured_bootstrap();
79 let before = snapshot(&config);
80
81 assert_eq!(
82 apply_standard_otlp_environment_values(&mut config, None, Some(OsStr::new("http/protobuf"))).unwrap(),
83 StandardOtlpEnvironmentStatus::Unchanged
84 );
85 assert_eq!(snapshot(&config), before);
86
87 assert_eq!(
88 apply_standard_otlp_environment_values(&mut config, Some(OsStr::new(" ")), Some(OsStr::new("grpc")))
89 .unwrap(),
90 StandardOtlpEnvironmentStatus::Unchanged
91 );
92 assert_eq!(snapshot(&config), before);
93 }
94
95 #[test]
96 fn exact_grpc_protocol_enables_all_otlp_signals() {
97 let mut config = configured_bootstrap();
98
99 let status = apply_standard_otlp_environment_values(
100 &mut config,
101 Some(OsStr::new(" http://collector:4317 ")),
102 Some(OsStr::new("grpc")),
103 )
104 .unwrap();
105
106 assert_eq!(status, StandardOtlpEnvironmentStatus::Applied);
107 assert!(config.observability.enabled);
108 assert!(config.observability.metrics.enabled);
109 assert_eq!(config.observability.metrics.exporter, MetricsExporter::OtlpGrpc);
110 assert!(config.observability.traces.enabled);
111 assert_eq!(config.observability.traces.exporter, TraceExporter::OtlpGrpc);
112 assert!(config.observability.logs.enabled);
113 assert_eq!(config.observability.logs.exporter, LogsExporter::OtlpGrpc);
114 assert_eq!(config.observability.otlp.endpoint, "http://collector:4317");
115 assert_eq!(config.observability.otlp.protocol, OtlpProtocol::Grpc);
116 }
117
118 #[test]
119 fn missing_or_non_grpc_protocol_fails_without_mutating_configuration() {
120 let mut config = configured_bootstrap();
121 let before = snapshot(&config);
122
123 let missing =
124 apply_standard_otlp_environment_values(&mut config, Some(OsStr::new("http://collector:4317")), None)
125 .unwrap_err();
126 assert!(!missing.to_string().contains("http://collector:4317"));
127 assert_eq!(snapshot(&config), before);
128
129 let invalid = apply_standard_otlp_environment_values(
130 &mut config,
131 Some(OsStr::new("http://user:secret@collector:4317")),
132 Some(OsStr::new("http/protobuf")),
133 )
134 .unwrap_err();
135 assert!(!invalid.to_string().contains("secret"));
136 assert_eq!(snapshot(&config), before);
137 }
138
139 #[cfg(any(unix, windows))]
140 #[test]
141 fn non_utf8_values_fail_without_mutating_configuration() {
142 let mut config = configured_bootstrap();
143 let before = snapshot(&config);
144 let invalid = invalid_os_string();
145
146 assert!(apply_standard_otlp_environment_values(
147 &mut config,
148 Some(invalid.as_os_str()),
149 Some(OsStr::new("grpc"))
150 )
151 .is_err());
152 assert_eq!(snapshot(&config), before);
153
154 assert!(apply_standard_otlp_environment_values(
155 &mut config,
156 Some(OsStr::new("http://collector:4317")),
157 Some(invalid.as_os_str())
158 )
159 .is_err());
160 assert_eq!(snapshot(&config), before);
161 }
162
163 fn configured_bootstrap() -> TelemetryBootstrapConfig {
164 let mut config = TelemetryBootstrapConfig::default();
165 config.observability.enabled = true;
166 config.observability.metrics.enabled = true;
167 config.observability.metrics.exporter = MetricsExporter::Log;
168 config.observability.traces.enabled = false;
169 config.observability.traces.exporter = TraceExporter::Disable;
170 config.observability.logs.enabled = true;
171 config.observability.logs.exporter = LogsExporter::Log;
172 config.observability.otlp.endpoint = "http://existing:4317".to_owned();
173 config
174 }
175
176 fn snapshot(
177 config: &TelemetryBootstrapConfig,
178 ) -> (
179 bool,
180 bool,
181 MetricsExporter,
182 bool,
183 TraceExporter,
184 bool,
185 LogsExporter,
186 String,
187 OtlpProtocol,
188 ) {
189 (
190 config.observability.enabled,
191 config.observability.metrics.enabled,
192 config.observability.metrics.exporter,
193 config.observability.traces.enabled,
194 config.observability.traces.exporter,
195 config.observability.logs.enabled,
196 config.observability.logs.exporter,
197 config.observability.otlp.endpoint.clone(),
198 config.observability.otlp.protocol,
199 )
200 }
201
202 #[cfg(unix)]
203 fn invalid_os_string() -> OsString {
204 use std::os::unix::ffi::OsStringExt;
205
206 OsString::from_vec(vec![0xff])
207 }
208
209 #[cfg(windows)]
210 fn invalid_os_string() -> OsString {
211 use std::os::windows::ffi::OsStringExt;
212
213 OsString::from_wide(&[0xd800])
214 }
215}