1use opentelemetry_otlp::{OTEL_EXPORTER_OTLP_PROTOCOL, Protocol};
7
8fn parse_protocol(value: &str) -> Option<Protocol> {
18 match value.trim().to_ascii_lowercase().as_str() {
19 "grpc" => Some(Protocol::Grpc),
20 "http/protobuf" | "http/proto" => Some(Protocol::HttpBinary),
21 "http/json" => Some(Protocol::HttpJson),
22 _ => None,
23 }
24}
25
26fn protocol_from_env(key: &str) -> Option<Protocol> {
36 std::env::var(key)
37 .ok()
38 .and_then(|value| parse_protocol(&value))
39}
40
41pub fn protocol_for_signal(signal_env: &str) -> Protocol {
51 protocol_from_env(signal_env)
52 .or_else(|| protocol_from_env(OTEL_EXPORTER_OTLP_PROTOCOL))
53 .unwrap_or(Protocol::Grpc)
54}
55
56macro_rules! build_exporter {
80 ($builder:expr, $protocol_env:expr, $msg:literal) => {
81 build_exporter!($builder, $protocol_env, $msg, |b| b)
82 };
83 ($builder:expr, $protocol_env:expr, $msg:literal, |$binder:ident| $config:expr) => {{
84 use ::anyhow::Context as _;
85 use ::opentelemetry_otlp::Protocol;
86 use ::opentelemetry_otlp::WithExportConfig as _;
87
88 let protocol = $crate::macros::protocol_for_signal($protocol_env);
89 match protocol {
90 Protocol::Grpc => {
91 let $binder = $builder.with_tonic();
92 let builder = $config;
93 builder.build().context(format!("{} (gRPC)", $msg))
94 }
95 _ => {
96 let $binder = $builder.with_http().with_protocol(protocol);
97 let builder = $config;
98 builder.build().context(format!("{} (HTTP)", $msg))
99 }
100 }
101 }};
102}
103
104pub(crate) use build_exporter;
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_parse_protocol() {
112 assert_eq!(parse_protocol("grpc"), Some(Protocol::Grpc));
114 assert_eq!(parse_protocol("http/protobuf"), Some(Protocol::HttpBinary));
115 assert_eq!(parse_protocol("http/proto"), Some(Protocol::HttpBinary));
116 assert_eq!(parse_protocol("http/json"), Some(Protocol::HttpJson));
117
118 assert_eq!(parse_protocol("GRPC"), Some(Protocol::Grpc));
120 assert_eq!(parse_protocol("HTTP/PROTOBUF"), Some(Protocol::HttpBinary));
121 assert_eq!(parse_protocol("HTTP/PROTO"), Some(Protocol::HttpBinary));
122 assert_eq!(parse_protocol("HTTP/JSON"), Some(Protocol::HttpJson));
123
124 assert_eq!(parse_protocol("Grpc"), Some(Protocol::Grpc));
126 assert_eq!(parse_protocol("Http/Protobuf"), Some(Protocol::HttpBinary));
127 assert_eq!(parse_protocol("Http/Proto"), Some(Protocol::HttpBinary));
128 assert_eq!(parse_protocol("Http/Json"), Some(Protocol::HttpJson));
129
130 assert_eq!(parse_protocol(" grpc "), Some(Protocol::Grpc));
132 assert_eq!(
133 parse_protocol(" http/protobuf "),
134 Some(Protocol::HttpBinary)
135 );
136 assert_eq!(parse_protocol("\thttp/proto\n"), Some(Protocol::HttpBinary));
137 assert_eq!(parse_protocol(" http/json "), Some(Protocol::HttpJson));
138
139 assert_eq!(parse_protocol("invalid"), None);
141 assert_eq!(parse_protocol(""), None);
142 assert_eq!(parse_protocol("http"), None);
143 assert_eq!(parse_protocol("grpc/http"), None);
144 assert_eq!(parse_protocol("json"), None);
145 }
146
147 #[test]
148 fn test_protocol_from_env() {
149 assert_eq!(protocol_from_env("NONEXISTENT_VAR_12345"), None);
151
152 unsafe {
154 std::env::set_var("TEST_PROTOCOL_GRPC", "grpc");
155 }
156 assert_eq!(
157 protocol_from_env("TEST_PROTOCOL_GRPC"),
158 Some(Protocol::Grpc)
159 );
160 unsafe {
161 std::env::remove_var("TEST_PROTOCOL_GRPC");
162 }
163
164 unsafe {
165 std::env::set_var("TEST_PROTOCOL_HTTP_BINARY", "http/protobuf");
166 }
167 assert_eq!(
168 protocol_from_env("TEST_PROTOCOL_HTTP_BINARY"),
169 Some(Protocol::HttpBinary)
170 );
171 unsafe {
172 std::env::remove_var("TEST_PROTOCOL_HTTP_BINARY");
173 }
174
175 unsafe {
176 std::env::set_var("TEST_PROTOCOL_HTTP_JSON", "http/json");
177 }
178 assert_eq!(
179 protocol_from_env("TEST_PROTOCOL_HTTP_JSON"),
180 Some(Protocol::HttpJson)
181 );
182 unsafe {
183 std::env::remove_var("TEST_PROTOCOL_HTTP_JSON");
184 }
185
186 unsafe {
188 std::env::set_var("TEST_PROTOCOL_INVALID", "invalid");
189 }
190 assert_eq!(protocol_from_env("TEST_PROTOCOL_INVALID"), None);
191 unsafe {
192 std::env::remove_var("TEST_PROTOCOL_INVALID");
193 }
194
195 unsafe {
197 std::env::set_var("TEST_PROTOCOL_WHITESPACE", " grpc ");
198 }
199 assert_eq!(
200 protocol_from_env("TEST_PROTOCOL_WHITESPACE"),
201 Some(Protocol::Grpc)
202 );
203 unsafe {
204 std::env::remove_var("TEST_PROTOCOL_WHITESPACE");
205 }
206 }
207
208 #[test]
209 fn test_protocol_for_signal() {
210 unsafe {
213 std::env::remove_var("TEST_SIGNAL_SPECIFIC");
214 std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
215 }
216 assert_eq!(protocol_for_signal("TEST_SIGNAL_SPECIFIC"), Protocol::Grpc);
217
218 unsafe {
220 std::env::set_var("TEST_SIGNAL_SPECIFIC", "http/json");
221 }
222 assert_eq!(
223 protocol_for_signal("TEST_SIGNAL_SPECIFIC"),
224 Protocol::HttpJson
225 );
226 unsafe {
227 std::env::remove_var("TEST_SIGNAL_SPECIFIC");
228 }
229
230 unsafe {
232 std::env::set_var(OTEL_EXPORTER_OTLP_PROTOCOL, "http/protobuf");
233 }
234 assert_eq!(
235 protocol_for_signal("TEST_SIGNAL_SPECIFIC"),
236 Protocol::HttpBinary
237 );
238 unsafe {
239 std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
240 }
241
242 unsafe {
244 std::env::set_var("TEST_SIGNAL_SPECIFIC", "http/json");
245 std::env::set_var(OTEL_EXPORTER_OTLP_PROTOCOL, "http/protobuf");
246 }
247 assert_eq!(
248 protocol_for_signal("TEST_SIGNAL_SPECIFIC"),
249 Protocol::HttpJson
250 );
251 unsafe {
252 std::env::remove_var("TEST_SIGNAL_SPECIFIC");
253 std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
254 }
255
256 unsafe {
258 std::env::set_var("TEST_SIGNAL_SPECIFIC", "invalid");
259 std::env::set_var(OTEL_EXPORTER_OTLP_PROTOCOL, "grpc");
260 }
261 assert_eq!(protocol_for_signal("TEST_SIGNAL_SPECIFIC"), Protocol::Grpc);
262 unsafe {
263 std::env::remove_var("TEST_SIGNAL_SPECIFIC");
264 std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
265 }
266
267 unsafe {
269 std::env::set_var("TEST_SIGNAL_SPECIFIC", "invalid");
270 std::env::set_var(OTEL_EXPORTER_OTLP_PROTOCOL, "also_invalid");
271 }
272 assert_eq!(protocol_for_signal("TEST_SIGNAL_SPECIFIC"), Protocol::Grpc);
273 unsafe {
274 std::env::remove_var("TEST_SIGNAL_SPECIFIC");
275 std::env::remove_var(OTEL_EXPORTER_OTLP_PROTOCOL);
276 }
277 }
278}