use std::collections::HashMap;
use std::sync::Arc;
pub trait LogParser: Send + Sync {
fn name(&self) -> &'static str;
fn can_parse(&self, sample_lines: &[&str]) -> bool;
fn parse_line(&self, line: &str) -> ParsedLogLine;
}
#[derive(Debug, Clone, Default)]
pub struct ParsedLogLine {
pub timestamp: Option<String>,
pub level: Option<String>,
pub message: Option<String>,
pub fields: HashMap<String, String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogFormat {
Generic,
Json,
Apache,
Syslog,
}
pub struct ParserRegistry {
parsers: Vec<(LogFormat, Arc<dyn LogParser>)>,
}
impl Default for ParserRegistry {
fn default() -> Self {
Self::new()
}
}
impl ParserRegistry {
pub fn new() -> Self {
let mut registry = Self {
parsers: Vec::new(),
};
let generic_parser = Arc::new(generic::GenericLogParser);
registry.register_parser(LogFormat::Generic, generic_parser);
let json_parser = Arc::new(json::JsonLogParser::new());
registry.register_parser(LogFormat::Json, json_parser);
registry
}
pub fn register_parser(&mut self, format: LogFormat, parser: Arc<dyn LogParser>) {
self.parsers.push((format, parser));
}
pub fn detect_format(&self, sample_lines: &[&str]) -> (LogFormat, Arc<dyn LogParser>) {
let sample = if sample_lines.len() < 5 {
sample_lines
} else {
&sample_lines[..5] };
let json_parser = self.get_parser(LogFormat::Json).unwrap();
if json_parser.can_parse(sample) {
return (LogFormat::Json, json_parser);
}
for (format, parser) in &self.parsers {
if *format != LogFormat::Json && parser.can_parse(sample) {
return (*format, Arc::clone(parser));
}
}
(
LogFormat::Generic,
self.get_parser(LogFormat::Generic).unwrap(),
)
}
pub fn get_parser(&self, format: LogFormat) -> Option<Arc<dyn LogParser>> {
self.parsers
.iter()
.find(|(f, _)| *f == format)
.map(|(_, p)| Arc::clone(p))
}
}
pub mod generic;
pub mod json;