1use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8
9#[derive(Debug, Clone, Serialize, Deserialize, Default)]
15pub struct ObservabilityConfig {
16 #[serde(default)]
18 pub metrics: MetricsConfig,
19
20 #[serde(default)]
22 pub logging: LoggingConfig,
23
24 #[serde(default)]
26 pub tracing: Option<TracingConfig>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct MetricsConfig {
36 #[serde(default = "default_true")]
38 pub enabled: bool,
39
40 #[serde(default = "default_metrics_address")]
42 pub address: String,
43
44 #[serde(default = "default_metrics_path")]
46 pub path: String,
47
48 #[serde(default)]
50 pub high_cardinality: bool,
51}
52
53impl Default for MetricsConfig {
54 fn default() -> Self {
55 Self {
56 enabled: default_true(),
57 address: default_metrics_address(),
58 path: default_metrics_path(),
59 high_cardinality: false,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct LoggingConfig {
71 #[serde(default = "default_log_level")]
73 pub level: String,
74
75 #[serde(default = "default_log_format")]
77 pub format: String,
78
79 #[serde(default = "default_true")]
81 pub timestamps: bool,
82
83 pub file: Option<PathBuf>,
85
86 #[serde(default)]
88 pub access_log: Option<AccessLogConfig>,
89
90 #[serde(default)]
92 pub error_log: Option<ErrorLogConfig>,
93
94 #[serde(default)]
96 pub audit_log: Option<AuditLogConfig>,
97}
98
99impl Default for LoggingConfig {
100 fn default() -> Self {
101 Self {
102 level: default_log_level(),
103 format: default_log_format(),
104 timestamps: default_true(),
105 file: None,
106 access_log: None,
107 error_log: None,
108 audit_log: None,
109 }
110 }
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct AccessLogFields {
116 #[serde(default = "default_true")]
117 pub timestamp: bool,
118 #[serde(default = "default_true")]
119 pub trace_id: bool,
120 #[serde(default = "default_true")]
121 pub method: bool,
122 #[serde(default = "default_true")]
123 pub path: bool,
124 #[serde(default = "default_true")]
125 pub query: bool,
126 #[serde(default = "default_true")]
127 pub status: bool,
128 #[serde(default = "default_true")]
129 pub latency_ms: bool,
130 #[serde(default = "default_true")]
131 pub body_bytes_sent: bool,
132 #[serde(default = "default_true")]
133 pub upstream_addr: bool,
134 #[serde(default = "default_true")]
135 pub connection_reused: bool,
136 #[serde(default = "default_true")]
137 pub rate_limit_hit: bool,
138 #[serde(default = "default_true")]
139 pub geo_country: bool,
140 #[serde(default = "default_true")]
141 pub user_agent: bool,
142 #[serde(default = "default_true")]
143 pub referer: bool,
144 #[serde(default = "default_true")]
145 pub client_ip: bool,
146}
147
148impl Default for AccessLogFields {
149 fn default() -> Self {
150 Self {
151 timestamp: true,
152 trace_id: true,
153 method: true,
154 path: true,
155 query: true,
156 status: true,
157 latency_ms: true,
158 body_bytes_sent: true,
159 upstream_addr: true,
160 connection_reused: true,
161 rate_limit_hit: true,
162 geo_country: true,
163 user_agent: true,
164 referer: true,
165 client_ip: true,
166 }
167 }
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct AccessLogConfig {
173 #[serde(default = "default_true")]
175 pub enabled: bool,
176
177 #[serde(default = "default_access_log_file")]
179 pub file: PathBuf,
180
181 #[serde(default = "default_access_log_format")]
183 pub format: String,
184
185 #[serde(default = "default_buffer_size")]
187 pub buffer_size: usize,
188
189 #[serde(default = "default_true")]
191 pub include_trace_id: bool,
192
193 #[serde(default = "default_sample_rate")]
195 pub sample_rate: f64,
196
197 #[serde(default = "default_true")]
199 pub sample_errors_always: bool,
200
201 #[serde(default)]
203 pub fields: AccessLogFields,
204}
205
206impl Default for AccessLogConfig {
207 fn default() -> Self {
208 Self {
209 enabled: true,
210 file: default_access_log_file(),
211 format: default_access_log_format(),
212 buffer_size: default_buffer_size(),
213 include_trace_id: true,
214 sample_rate: default_sample_rate(),
215 sample_errors_always: true,
216 fields: AccessLogFields::default(),
217 }
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct ErrorLogConfig {
224 #[serde(default = "default_true")]
226 pub enabled: bool,
227
228 #[serde(default = "default_error_log_file")]
230 pub file: PathBuf,
231
232 #[serde(default = "default_error_log_level")]
234 pub level: String,
235
236 #[serde(default = "default_buffer_size")]
238 pub buffer_size: usize,
239}
240
241impl Default for ErrorLogConfig {
242 fn default() -> Self {
243 Self {
244 enabled: true,
245 file: default_error_log_file(),
246 level: default_error_log_level(),
247 buffer_size: default_buffer_size(),
248 }
249 }
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct AuditLogConfig {
255 #[serde(default = "default_true")]
257 pub enabled: bool,
258
259 #[serde(default = "default_audit_log_file")]
261 pub file: PathBuf,
262
263 #[serde(default = "default_buffer_size")]
265 pub buffer_size: usize,
266
267 #[serde(default = "default_true")]
269 pub log_blocked: bool,
270
271 #[serde(default = "default_true")]
273 pub log_agent_decisions: bool,
274
275 #[serde(default = "default_true")]
277 pub log_waf_events: bool,
278}
279
280impl Default for AuditLogConfig {
281 fn default() -> Self {
282 Self {
283 enabled: true,
284 file: default_audit_log_file(),
285 buffer_size: default_buffer_size(),
286 log_blocked: true,
287 log_agent_decisions: true,
288 log_waf_events: true,
289 }
290 }
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct TracingConfig {
300 pub backend: TracingBackend,
302
303 #[serde(default = "default_sampling_rate")]
305 pub sampling_rate: f64,
306
307 #[serde(default = "default_service_name")]
309 pub service_name: String,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
314#[serde(rename_all = "snake_case")]
315pub enum TracingBackend {
316 Jaeger { endpoint: String },
317 Zipkin { endpoint: String },
318 Otlp { endpoint: String },
319}
320
321fn default_true() -> bool {
326 true
327}
328
329fn default_metrics_address() -> String {
330 "0.0.0.0:9090".to_string()
331}
332
333fn default_metrics_path() -> String {
334 "/metrics".to_string()
335}
336
337fn default_log_level() -> String {
338 "info".to_string()
339}
340
341fn default_log_format() -> String {
342 "json".to_string()
343}
344
345fn default_access_log_format() -> String {
346 "json".to_string()
347}
348
349fn default_buffer_size() -> usize {
350 8192
351}
352
353fn default_access_log_file() -> PathBuf {
354 PathBuf::from("/var/log/sentinel/access.log")
355}
356
357fn default_error_log_file() -> PathBuf {
358 PathBuf::from("/var/log/sentinel/error.log")
359}
360
361fn default_error_log_level() -> String {
362 "warn".to_string()
363}
364
365fn default_audit_log_file() -> PathBuf {
366 PathBuf::from("/var/log/sentinel/audit.log")
367}
368
369fn default_sampling_rate() -> f64 {
370 0.01
371}
372
373fn default_sample_rate() -> f64 {
374 1.0 }
376
377fn default_service_name() -> String {
378 "sentinel".to_string()
379}