camel_core/shared/observability/domain/
config.rs1use std::fmt;
2use std::sync::Arc;
3
4use serde::Deserialize;
5
6use camel_api::metrics::MetricsCollector;
7
8#[derive(Clone, Deserialize, Default)]
14pub struct TracerConfig {
15 #[serde(default)]
16 pub enabled: bool,
17
18 #[serde(default = "default_detail_level")]
19 pub detail_level: DetailLevel,
20
21 #[serde(default)]
22 pub outputs: TracerOutputs,
23
24 #[serde(skip)]
27 pub metrics_collector: Option<Arc<dyn MetricsCollector>>,
28}
29
30impl fmt::Debug for TracerConfig {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 f.debug_struct("TracerConfig")
33 .field("enabled", &self.enabled)
34 .field("detail_level", &self.detail_level)
35 .field("outputs", &self.outputs)
36 .field(
37 "metrics_collector",
38 &self.metrics_collector.as_ref().map(|_| "MetricsCollector"),
39 )
40 .finish()
41 }
42}
43
44#[derive(Debug, Clone, Deserialize, Default)]
45pub struct TracerOutputs {
46 #[serde(default)]
47 pub stdout: StdoutOutput,
48
49 #[serde(default)]
50 pub file: Option<FileOutput>,
51}
52
53#[derive(Debug, Clone, Deserialize)]
54pub struct StdoutOutput {
55 #[serde(default = "default_true")]
56 pub enabled: bool,
57
58 #[serde(default = "default_format")]
59 pub format: OutputFormat,
60}
61
62impl Default for StdoutOutput {
63 fn default() -> Self {
64 Self {
65 enabled: true,
66 format: OutputFormat::Json,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Deserialize)]
72pub struct FileOutput {
73 pub enabled: bool,
74 pub path: String,
75 #[serde(default = "default_format")]
76 pub format: OutputFormat,
77}
78
79#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
89#[serde(rename_all = "lowercase")]
90pub enum DetailLevel {
91 #[default]
92 Minimal,
93 Medium,
94 Full,
95}
96
97#[derive(Debug, Clone, Deserialize, Default)]
98#[serde(rename_all = "lowercase")]
99pub enum OutputFormat {
100 #[default]
101 Json,
102 Plain,
103}
104
105fn default_detail_level() -> DetailLevel {
106 DetailLevel::Minimal
107}
108fn default_format() -> OutputFormat {
109 OutputFormat::Json
110}
111fn default_true() -> bool {
112 true
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn tracer_config_defaults_are_stable() {
121 let cfg = TracerConfig::default();
122 assert!(!cfg.enabled);
123 assert_eq!(cfg.detail_level, DetailLevel::Minimal);
124 assert!(cfg.outputs.stdout.enabled);
125 assert!(matches!(cfg.outputs.stdout.format, OutputFormat::Json));
126 assert!(cfg.outputs.file.is_none());
127 assert!(cfg.metrics_collector.is_none());
128 }
129
130 #[test]
131 fn tracer_config_deserializes_lowercase_enums() {
132 let cfg: TracerConfig = serde_json::from_str(
133 r#"{
134 "enabled": true,
135 "detail_level": "full",
136 "outputs": {
137 "stdout": { "enabled": false, "format": "plain" },
138 "file": { "enabled": true, "path": "/tmp/trace.log", "format": "json" }
139 }
140}"#,
141 )
142 .unwrap();
143
144 assert!(cfg.enabled);
145 assert_eq!(cfg.detail_level, DetailLevel::Full);
146 assert!(!cfg.outputs.stdout.enabled);
147 assert!(matches!(cfg.outputs.stdout.format, OutputFormat::Plain));
148 assert_eq!(cfg.outputs.file.as_ref().unwrap().path, "/tmp/trace.log");
149 }
150
151 #[test]
152 fn tracer_config_debug_redacts_metrics_collector() {
153 let dbg = format!("{:?}", TracerConfig::default());
154 assert!(dbg.contains("TracerConfig"));
155 assert!(dbg.contains("metrics_collector"));
156 }
157}