use super::{EventInputDecoded, SyslogConfig, parse_json, parse_plain, parse_syslog};
pub fn auto_detect(line: &str, syslog_config: &SyslogConfig) -> EventInputDecoded {
let trimmed = line.trim_start();
if (trimmed.starts_with('{') || trimmed.starts_with('['))
&& let Some(decoded) = parse_json(line)
{
return decoded;
}
if trimmed.starts_with('<') {
let decoded = parse_syslog(line, syslog_config);
if has_syslog_fields(&decoded) {
return decoded;
}
}
parse_plain(line)
}
fn has_syslog_fields(decoded: &EventInputDecoded) -> bool {
match decoded {
EventInputDecoded::Kv(kv) => kv.fields().iter().any(|(k, _)| k != "_raw"),
EventInputDecoded::Json(_) => true,
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use rsigma_eval::Event;
fn cfg() -> SyslogConfig {
SyslogConfig::default()
}
#[test]
fn auto_detect_json() {
let decoded = auto_detect(r#"{"EventID": 1, "host": "web01"}"#, &cfg());
assert!(matches!(decoded, EventInputDecoded::Json(_)));
assert!(decoded.get_field("EventID").is_some());
}
#[test]
fn auto_detect_syslog() {
let decoded = auto_detect(
"<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick",
&cfg(),
);
assert!(
matches!(
decoded,
EventInputDecoded::Kv(_) | EventInputDecoded::Json(_)
),
"Expected Kv or Json for syslog, got Plain"
);
}
#[test]
fn auto_detect_plain() {
let decoded = auto_detect("ERROR: something went wrong on server", &cfg());
assert!(matches!(decoded, EventInputDecoded::Plain(_)));
}
#[test]
fn auto_detect_syslog_wrapped_json() {
let decoded = auto_detect(
r#"<134>1 2024-01-15T10:30:00Z host app - - - {"key": "value"}"#,
&cfg(),
);
assert!(
matches!(decoded, EventInputDecoded::Json(_)),
"Expected embedded JSON to be extracted"
);
}
#[test]
fn auto_detect_invalid_json_falls_through() {
let decoded = auto_detect("{not valid json}", &cfg());
assert!(matches!(decoded, EventInputDecoded::Plain(_)));
}
#[test]
fn mixed_format_batch() {
let c = cfg();
let lines = [
r#"{"EventID": 1}"#,
"<34>Oct 11 22:14:15 host su: test",
"plain log line",
];
let results: Vec<_> = lines.iter().map(|l| auto_detect(l, &c)).collect();
assert!(matches!(results[0], EventInputDecoded::Json(_)));
assert!(matches!(
results[1],
EventInputDecoded::Kv(_) | EventInputDecoded::Json(_)
));
assert!(matches!(results[2], EventInputDecoded::Plain(_)));
}
#[test]
fn auto_detect_syslog_respects_config() {
let config = SyslogConfig {
default_tz_offset_secs: 5 * 3600,
};
let decoded = auto_detect("<34>Oct 11 22:14:15 mymachine su: test", &config);
assert!(matches!(
decoded,
EventInputDecoded::Kv(_) | EventInputDecoded::Json(_)
));
}
}