Skip to main content

squib_api/schemas/
logger.rs

1//! `/logger` PUT body.
2
3use serde::{Deserialize, Serialize};
4
5use super::common::SafePath;
6
7/// Per Firecracker. Mirrors upstream's six-level vocabulary.
8#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)]
9pub enum LogLevel {
10    /// Suppress all logging.
11    Off,
12    /// Errors only.
13    Error,
14    /// Warnings and errors.
15    Warning,
16    /// Informational events.
17    #[default]
18    Info,
19    /// Per-request debug events.
20    Debug,
21    /// Verbose tracing events.
22    Trace,
23}
24
25/// Raw `/logger` PUT body off the wire.
26#[derive(Debug, Clone, Deserialize)]
27#[serde(deny_unknown_fields)]
28pub struct RawLoggerConfig {
29    /// Path to a regular file or named pipe.
30    pub log_path: String,
31    /// Log level filter.
32    #[serde(default)]
33    pub level: LogLevel,
34    /// Include the level in each line.
35    #[serde(default)]
36    pub show_level: bool,
37    /// Include the `file:line` origin in each line.
38    #[serde(default)]
39    pub show_log_origin: bool,
40    /// Tracing module filter.
41    #[serde(default)]
42    pub module: Option<String>,
43}
44
45/// Validated `/logger` PUT body.
46#[derive(Debug, Clone, Serialize)]
47#[non_exhaustive]
48pub struct LoggerConfig {
49    /// Validated log destination.
50    pub log_path: SafePath,
51    /// Log level.
52    pub level: LogLevel,
53    /// Include the level in each line.
54    pub show_level: bool,
55    /// Include `file:line` origin in each line.
56    pub show_log_origin: bool,
57    /// Optional module filter.
58    pub module: Option<String>,
59}
60
61impl TryFrom<RawLoggerConfig> for LoggerConfig {
62    type Error = String;
63
64    fn try_from(raw: RawLoggerConfig) -> Result<Self, Self::Error> {
65        let log_path = SafePath::new(raw.log_path).map_err(|e| format!("Invalid log_path: {e}"))?;
66        if let Some(m) = raw.module.as_deref()
67            && (m.is_empty() || m.len() > 128)
68        {
69            return Err("Invalid module: must be 1..=128 bytes".into());
70        }
71        Ok(Self {
72            log_path,
73            level: raw.level,
74            show_level: raw.show_level,
75            show_log_origin: raw.show_log_origin,
76            module: raw.module,
77        })
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_should_accept_minimal_logger_config() {
87        let cfg = LoggerConfig::try_from(RawLoggerConfig {
88            log_path: "/tmp/squib.log".into(),
89            level: LogLevel::Info,
90            show_level: true,
91            show_log_origin: false,
92            module: None,
93        })
94        .unwrap();
95        assert_eq!(cfg.level, LogLevel::Info);
96    }
97
98    #[test]
99    fn test_should_default_level_to_info() {
100        let raw: RawLoggerConfig =
101            serde_json::from_str(r#"{"log_path":"/tmp/squib.log"}"#).unwrap();
102        assert_eq!(raw.level, LogLevel::Info);
103    }
104}