Skip to main content

kaizen/telemetry/
resolve.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! Env + TOML resolution for third-party keys (see `TelemetryConfig`).
3
4use crate::core::config::ExporterConfig;
5
6const KAIZEN: &str = "KAIZEN_";
7
8/// Resolve env, preferring standard names, then `KAIZEN_` + same suffix.
9pub(crate) fn env_two(std_key: &str, kaizen_key: &str) -> Option<String> {
10    std::env::var(std_key)
11        .ok()
12        .filter(|s| !s.is_empty())
13        .or_else(|| std::env::var(kaizen_key).ok().filter(|s| !s.is_empty()))
14}
15
16/// Effective PostHog settings after env overlay.
17pub struct PostHogResolved {
18    pub host: String,
19    pub project_api_key: String,
20}
21
22/// Effective Datadog settings after env overlay. `app_key` is required for query/pull (Logs
23/// Search API v2) but not for log intake; we keep it optional and surface a clear error in
24/// `pull` when missing.
25pub struct DatadogResolved {
26    pub site: String,
27    pub api_key: String,
28    pub app_key: Option<String>,
29}
30
31/// Effective OTLP push endpoint.
32pub struct OtlpResolved {
33    pub endpoint: String,
34}
35
36impl PostHogResolved {
37    pub fn from_config(c: &ExporterConfig) -> Option<Self> {
38        let (host, key_opt) = match c {
39            ExporterConfig::PostHog {
40                host,
41                project_api_key,
42                ..
43            } => (host.as_deref(), project_api_key.as_deref()),
44            _ => return None,
45        };
46        let project_api_key = key_opt
47            .map(String::from)
48            .or_else(|| env_two("POSTHOG_API_KEY", "KAIZEN_POSTHOG_API_KEY"))?;
49        let host = host
50            .map(String::from)
51            .or_else(|| env_two("POSTHOG_HOST", "KAIZEN_POSTHOG_HOST"))
52            .unwrap_or_else(|| "https://us.i.posthog.com".to_string());
53        Some(Self {
54            host,
55            project_api_key,
56        })
57    }
58
59    /// Query / pull: API key and host from env only (no TOML row required).
60    pub fn from_env_only() -> Option<Self> {
61        let project_api_key = env_two("POSTHOG_API_KEY", "KAIZEN_POSTHOG_API_KEY")?;
62        let host = env_two("POSTHOG_HOST", "KAIZEN_POSTHOG_HOST")
63            .unwrap_or_else(|| "https://us.i.posthog.com".to_string());
64        Some(Self {
65            host,
66            project_api_key,
67        })
68    }
69}
70
71impl DatadogResolved {
72    pub fn from_config(c: &ExporterConfig) -> Option<Self> {
73        let (site, key_opt) = match c {
74            ExporterConfig::Datadog { site, api_key, .. } => (site.as_deref(), api_key.as_deref()),
75            _ => return None,
76        };
77        let api_key = key_opt
78            .map(String::from)
79            .or_else(|| env_two("DD_API_KEY", "KAIZEN_DD_API_KEY"))?;
80        let site = site
81            .map(String::from)
82            .or_else(|| env_two("DD_SITE", "KAIZEN_DD_SITE"))
83            .unwrap_or_else(|| "datadoghq.com".to_string());
84        let app_key = env_two("DD_APP_KEY", "KAIZEN_DD_APP_KEY");
85        Some(Self {
86            site,
87            api_key,
88            app_key,
89        })
90    }
91
92    /// Query / pull: API key and site from env only.
93    pub fn from_env_only() -> Option<Self> {
94        let api_key = env_two("DD_API_KEY", "KAIZEN_DD_API_KEY")?;
95        let site =
96            env_two("DD_SITE", "KAIZEN_DD_SITE").unwrap_or_else(|| "datadoghq.com".to_string());
97        let app_key = env_two("DD_APP_KEY", "KAIZEN_DD_APP_KEY");
98        Some(Self {
99            site,
100            api_key,
101            app_key,
102        })
103    }
104}
105
106impl OtlpResolved {
107    pub fn from_config(c: &ExporterConfig) -> Option<Self> {
108        let ep = match c {
109            ExporterConfig::Otlp { endpoint, .. } => endpoint.as_deref(),
110            _ => return None,
111        };
112        let endpoint = ep.map(String::from).or_else(|| {
113            env_two(
114                "OTEL_EXPORTER_OTLP_ENDPOINT",
115                "KAIZEN_OTEL_EXPORTER_OTLP_ENDPOINT",
116            )
117        })?;
118        Some(Self { endpoint })
119    }
120}
121
122/// Prevent unused `KAIZEN` const noise if we add more keys later; keeps resolution discoverable in docs.
123#[allow(dead_code)]
124fn _kaizen_prefix() -> &'static str {
125    KAIZEN
126}