Skip to main content

klauthed_observability/config/
telemetry.rs

1//! The top-level [`TelemetryConfig`].
2
3use klauthed_core::config::Profile;
4use serde::{Deserialize, Serialize};
5
6use super::{LogConfig, LogFormat, MetricsConfig, OtelConfig};
7
8/// Top-level telemetry settings for a service.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct TelemetryConfig {
11    /// Service name reported in logs, metrics, and traces.
12    #[serde(default = "default_service_name")]
13    pub service_name: String,
14    /// Logging / tracing-subscriber settings.
15    #[serde(default)]
16    pub log: LogConfig,
17    /// Prometheus metrics settings.
18    #[serde(default)]
19    pub metrics: MetricsConfig,
20    /// OpenTelemetry (OTLP) trace export settings.
21    #[serde(default)]
22    pub otel: OtelConfig,
23}
24
25fn default_service_name() -> String {
26    "service".to_owned()
27}
28
29impl Default for TelemetryConfig {
30    fn default() -> Self {
31        Self {
32            service_name: default_service_name(),
33            log: LogConfig::default(),
34            metrics: MetricsConfig::default(),
35            otel: OtelConfig::default(),
36        }
37    }
38}
39
40impl TelemetryConfig {
41    /// A config for `service_name` with profile-appropriate defaults: human
42    /// `Pretty` logs on local/dev/test, structured `Json` logs on staging/prod.
43    pub fn for_profile(profile: &Profile, service_name: impl Into<String>) -> Self {
44        let format = if profile.requires_vault() { LogFormat::Json } else { LogFormat::Pretty };
45        Self {
46            service_name: service_name.into(),
47            log: LogConfig { format, ..LogConfig::default() },
48            ..Self::default()
49        }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use serde_json::json;
57
58    #[test]
59    fn defaults_are_sensible() {
60        let cfg = TelemetryConfig::default();
61        assert_eq!(cfg.service_name, "service");
62        assert_eq!(cfg.log.format, LogFormat::Pretty);
63        assert_eq!(cfg.log.level, "info");
64        assert!(!cfg.metrics.enabled);
65        assert!(!cfg.otel.enabled);
66        assert_eq!(cfg.otel.endpoint, "http://localhost:4318");
67    }
68
69    #[test]
70    fn profile_selects_log_format() {
71        assert_eq!(
72            TelemetryConfig::for_profile(&Profile::Local, "svc").log.format,
73            LogFormat::Pretty
74        );
75        assert_eq!(TelemetryConfig::for_profile(&Profile::Prod, "svc").log.format, LogFormat::Json);
76    }
77
78    #[test]
79    fn deserializes_partial_config() {
80        let cfg: TelemetryConfig = serde_json::from_value(json!({
81            "service_name": "api",
82            "log": { "format": "json", "level": "debug" },
83            "otel": { "enabled": true }
84        }))
85        .unwrap();
86        assert_eq!(cfg.service_name, "api");
87        assert_eq!(cfg.log.format, LogFormat::Json);
88        assert_eq!(cfg.log.level, "debug");
89        assert!(cfg.otel.enabled);
90        assert!(cfg.log.ansi); // default preserved
91    }
92}