use crate::{
ConsoleSink, FileSink, FileSinkConfig, LogDispatcher, LogError, LogPolicy, LogSink,
RingBufferSink,
};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogOutputMode {
Disabled,
Terminal,
File,
TerminalAndFile,
CrashOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogConfigError {
MissingFile,
Sink(LogError),
}
impl From<LogError> for LogConfigError {
fn from(error: LogError) -> Self {
Self::Sink(error)
}
}
#[derive(Debug, Clone)]
pub struct LoggerConfig {
pub policy: LogPolicy,
pub output: LogOutputMode,
pub file: Option<FileSinkConfig>,
pub crash_events: usize,
pub crash_bytes: usize,
}
impl Default for LoggerConfig {
fn default() -> Self {
Self {
policy: LogPolicy::default(),
output: LogOutputMode::Terminal,
file: None,
crash_events: 256,
crash_bytes: 1024 * 1024,
}
}
}
impl LoggerConfig {
pub fn build(self) -> Result<ConfiguredLogger, LogConfigError> {
let Self {
policy,
output,
file,
crash_events,
crash_bytes,
} = self;
let mut sinks = Vec::<Arc<dyn LogSink>>::with_capacity(2);
let mut crash_ring = None;
let mut crash_sink = None;
if matches!(
output,
LogOutputMode::Terminal | LogOutputMode::TerminalAndFile
) {
sinks.push(Arc::new(ConsoleSink::new()));
}
if matches!(output, LogOutputMode::File | LogOutputMode::TerminalAndFile) {
let file = file.ok_or(LogConfigError::MissingFile)?;
sinks.push(Arc::new(FileSink::new(file)?));
} else if output == LogOutputMode::CrashOnly {
let file = file.ok_or(LogConfigError::MissingFile)?;
let ring = Arc::new(RingBufferSink::new(crash_events, crash_bytes)?);
sinks.push(ring.clone());
crash_ring = Some(ring);
crash_sink = Some(FileSink::new(file)?);
}
Ok(ConfiguredLogger {
dispatcher: LogDispatcher::new(policy, sinks),
crash_ring,
crash_sink,
})
}
}
pub struct ConfiguredLogger {
dispatcher: LogDispatcher,
crash_ring: Option<Arc<RingBufferSink>>,
crash_sink: Option<FileSink>,
}
impl ConfiguredLogger {
pub fn dispatcher(&self) -> &LogDispatcher {
&self.dispatcher
}
pub fn dump_crash(&self) -> Result<usize, LogConfigError> {
let Some(ring) = &self.crash_ring else {
return Ok(0);
};
let sink = self
.crash_sink
.as_ref()
.ok_or(LogConfigError::MissingFile)?;
let events = ring.snapshot();
for event in &events {
sink.emit(event)?;
}
Ok(events.len())
}
}