subcog/observability/
otlp.rs1use crate::config::OtlpSettings;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum OtlpProtocol {
8 Grpc,
10 Http,
12}
13
14impl OtlpProtocol {
15 #[must_use]
17 pub fn parse(value: &str) -> Option<Self> {
18 match value.to_lowercase().as_str() {
19 "grpc" => Some(Self::Grpc),
20 "http" | "http/protobuf" | "http_binary" | "http-binary" => Some(Self::Http),
21 _ => None,
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct OtlpConfig {
29 pub endpoint: Option<String>,
31 pub protocol: OtlpProtocol,
33}
34
35impl OtlpConfig {
36 #[must_use]
38 pub fn from_env() -> Self {
39 Self::from_settings(None)
40 }
41
42 #[must_use]
44 pub fn from_settings(settings: Option<&OtlpSettings>) -> Self {
45 let protocol_explicit = settings
46 .and_then(|config| config.protocol.as_deref())
47 .is_some();
48 let endpoint = settings.and_then(|config| config.endpoint.clone());
49 let protocol = settings
50 .and_then(|config| config.protocol.as_deref())
51 .and_then(OtlpProtocol::parse)
52 .unwrap_or_else(|| protocol_from_endpoint(endpoint.as_deref()));
53
54 let mut config = Self { endpoint, protocol };
55 config.apply_env_overrides(protocol_explicit);
56 config
57 }
58}
59
60pub struct OtlpExporter;
62
63impl OtlpExporter {
64 #[must_use]
66 pub const fn new() -> Self {
67 Self
68 }
69}
70
71impl Default for OtlpExporter {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77pub(super) fn endpoint_from_env() -> Option<String> {
78 std::env::var("SUBCOG_OTLP_ENDPOINT")
79 .ok()
80 .or_else(|| std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok())
81}
82
83fn protocol_from_env_override() -> Option<OtlpProtocol> {
84 std::env::var("SUBCOG_OTLP_PROTOCOL")
85 .or_else(|_| std::env::var("OTEL_EXPORTER_OTLP_PROTOCOL"))
86 .ok()
87 .and_then(|value| OtlpProtocol::parse(&value))
88}
89
90fn protocol_from_endpoint(endpoint: Option<&str>) -> OtlpProtocol {
91 if endpoint.is_some_and(|endpoint| endpoint.contains(":4317")) {
92 return OtlpProtocol::Grpc;
93 }
94
95 OtlpProtocol::Http
96}
97
98impl OtlpConfig {
99 fn apply_env_overrides(&mut self, protocol_explicit: bool) {
100 let endpoint_override = endpoint_from_env();
101 let endpoint_overridden = endpoint_override.is_some();
102 if let Some(endpoint) = endpoint_override {
103 self.endpoint = Some(endpoint);
104 }
105
106 if let Some(protocol) = protocol_from_env_override() {
107 self.protocol = protocol;
108 return;
109 }
110
111 if endpoint_overridden && !protocol_explicit {
112 self.protocol = protocol_from_endpoint(self.endpoint.as_deref());
113 }
114 }
115}