sentinel_config/
observability.rs

1//! Observability configuration types
2//!
3//! This module contains configuration types for metrics, logging,
4//! and distributed tracing.
5
6use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8
9// ============================================================================
10// Observability Configuration
11// ============================================================================
12
13/// Observability configuration
14#[derive(Debug, Clone, Serialize, Deserialize, Default)]
15pub struct ObservabilityConfig {
16    /// Metrics configuration
17    #[serde(default)]
18    pub metrics: MetricsConfig,
19
20    /// Logging configuration
21    #[serde(default)]
22    pub logging: LoggingConfig,
23
24    /// Tracing configuration
25    #[serde(default)]
26    pub tracing: Option<TracingConfig>,
27}
28
29// ============================================================================
30// Metrics Configuration
31// ============================================================================
32
33/// Metrics configuration
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct MetricsConfig {
36    /// Enable metrics collection
37    #[serde(default = "default_true")]
38    pub enabled: bool,
39
40    /// Metrics endpoint address
41    #[serde(default = "default_metrics_address")]
42    pub address: String,
43
44    /// Metrics path
45    #[serde(default = "default_metrics_path")]
46    pub path: String,
47
48    /// Include high-cardinality metrics
49    #[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// ============================================================================
65// Logging Configuration
66// ============================================================================
67
68/// Logging configuration
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct LoggingConfig {
71    /// Log level
72    #[serde(default = "default_log_level")]
73    pub level: String,
74
75    /// Log format (json, pretty)
76    #[serde(default = "default_log_format")]
77    pub format: String,
78
79    /// Include timestamps
80    #[serde(default = "default_true")]
81    pub timestamps: bool,
82
83    /// Application log file path (stdout if not specified)
84    pub file: Option<PathBuf>,
85
86    /// Access log configuration
87    #[serde(default)]
88    pub access_log: Option<AccessLogConfig>,
89
90    /// Error log configuration
91    #[serde(default)]
92    pub error_log: Option<ErrorLogConfig>,
93
94    /// Audit log configuration (security events)
95    #[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/// Access log field selection
114#[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/// Access log configuration
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct AccessLogConfig {
173    /// Enable access logging
174    #[serde(default = "default_true")]
175    pub enabled: bool,
176
177    /// Access log file path
178    #[serde(default = "default_access_log_file")]
179    pub file: PathBuf,
180
181    /// Log format (combined, json, custom)
182    #[serde(default = "default_access_log_format")]
183    pub format: String,
184
185    /// Buffer size for writes
186    #[serde(default = "default_buffer_size")]
187    pub buffer_size: usize,
188
189    /// Include trace_id in logs
190    #[serde(default = "default_true")]
191    pub include_trace_id: bool,
192
193    /// Sampling rate (0.0-1.0, 1.0 = log all requests)
194    #[serde(default = "default_sample_rate")]
195    pub sample_rate: f64,
196
197    /// Always log errors (4xx/5xx status codes) regardless of sample_rate
198    #[serde(default = "default_true")]
199    pub sample_errors_always: bool,
200
201    /// Field selection (which fields to include in logs)
202    #[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/// Error log configuration
222#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct ErrorLogConfig {
224    /// Enable error logging
225    #[serde(default = "default_true")]
226    pub enabled: bool,
227
228    /// Error log file path
229    #[serde(default = "default_error_log_file")]
230    pub file: PathBuf,
231
232    /// Minimum level for error log (warn, error)
233    #[serde(default = "default_error_log_level")]
234    pub level: String,
235
236    /// Buffer size for writes
237    #[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/// Audit log configuration (security events)
253#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct AuditLogConfig {
255    /// Enable audit logging
256    #[serde(default = "default_true")]
257    pub enabled: bool,
258
259    /// Audit log file path
260    #[serde(default = "default_audit_log_file")]
261    pub file: PathBuf,
262
263    /// Buffer size for writes
264    #[serde(default = "default_buffer_size")]
265    pub buffer_size: usize,
266
267    /// Log blocked requests
268    #[serde(default = "default_true")]
269    pub log_blocked: bool,
270
271    /// Log agent decisions
272    #[serde(default = "default_true")]
273    pub log_agent_decisions: bool,
274
275    /// Log WAF events
276    #[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// ============================================================================
294// Tracing Configuration
295// ============================================================================
296
297/// Tracing configuration
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct TracingConfig {
300    /// Tracing backend
301    pub backend: TracingBackend,
302
303    /// Sampling rate (0.0 - 1.0)
304    #[serde(default = "default_sampling_rate")]
305    pub sampling_rate: f64,
306
307    /// Service name
308    #[serde(default = "default_service_name")]
309    pub service_name: String,
310}
311
312/// Tracing backend
313#[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
321// ============================================================================
322// Default Value Functions
323// ============================================================================
324
325fn 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 // Log all requests by default (100%)
375}
376
377fn default_service_name() -> String {
378    "sentinel".to_string()
379}