use std::time::Duration; use serde::{Deserialize, Serialize}; use crate::duration::deserialize_std_duration; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] pub struct ObsSettings { pub enabled: bool, pub service_name: String, pub service_version: String, pub stream_name: String, pub endpoint: String, pub token: Option, pub user: Option, pub password: Option, pub enable_logs: bool, pub enable_metrics: bool, pub enable_traces: bool, pub traces_sample_ratio: f64, pub batch_size: usize, #[serde(default, deserialize_with = "deserialize_std_duration")] pub batch_interval: Duration, #[serde(default, deserialize_with = "deserialize_std_duration")] pub timeout: Duration, pub console_log: bool, pub log_level: String, } impl Default for ObsSettings { fn default() -> Self { Self { enabled: false, service_name: String::new(), service_version: String::new(), stream_name: String::new(), endpoint: "http://127.0.0.1:5080/api/default/v1".to_string(), token: None, user: None, password: None, enable_logs: true, enable_metrics: true, enable_traces: true, traces_sample_ratio: 1.0, batch_size: 512, batch_interval: Duration::from_secs(5), timeout: Duration::from_secs(10), console_log: true, log_level: String::new(), } } } impl ObsSettings { pub fn with_app_defaults( mut self, service_name: impl Into, log_level: impl Into, ) -> Self { if self.service_name.is_empty() { self.service_name = service_name.into(); } if self.log_level.is_empty() { self.log_level = log_level.into(); } self } pub fn is_valid(&self) -> Result<(), String> { if !self.enabled { return Ok(()); } if self.endpoint.is_empty() { return Err("obs.endpoint must not be empty when obs.enabled".into()); } if !(0.0..=1.0).contains(&self.traces_sample_ratio) { return Err(format!( "obs.traces_sample_ratio ({}) must be in [0.0, 1.0]", self.traces_sample_ratio )); } if self.token.is_some() && (self.user.is_some() || self.password.is_some()) { return Err( "obs: choose either token (Bearer) or user/password (Basic), not both".into(), ); } if self.user.is_some() != self.password.is_some() { return Err("obs: user and password must be set together for Basic auth".into()); } if self.batch_size == 0 { return Err("obs.batch_size must be >= 1".into()); } if self.batch_interval.is_zero() || self.timeout.is_zero() { return Err("obs.batch_interval and obs.timeout must be > 0".into()); } Ok(()) } }