1use crate::{
14 ConsoleSink, FileSink, FileSinkConfig, LogDispatcher, LogError, LogPolicy, LogSink,
15 RingBufferSink,
16};
17use std::sync::Arc;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum LogOutputMode {
22 Disabled,
24 Terminal,
26 File,
28 TerminalAndFile,
30 CrashOnly,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum LogConfigError {
37 MissingFile,
39 Sink(LogError),
41}
42
43impl From<LogError> for LogConfigError {
44 fn from(error: LogError) -> Self {
45 Self::Sink(error)
46 }
47}
48
49#[derive(Debug, Clone)]
51pub struct LoggerConfig {
52 pub policy: LogPolicy,
54 pub output: LogOutputMode,
56 pub file: Option<FileSinkConfig>,
58 pub crash_events: usize,
60 pub crash_bytes: usize,
62}
63
64impl Default for LoggerConfig {
65 fn default() -> Self {
66 Self {
67 policy: LogPolicy::default(),
68 output: LogOutputMode::Terminal,
69 file: None,
70 crash_events: 256,
71 crash_bytes: 1024 * 1024,
72 }
73 }
74}
75
76impl LoggerConfig {
77 pub fn build(self) -> Result<ConfiguredLogger, LogConfigError> {
79 let Self {
80 policy,
81 output,
82 file,
83 crash_events,
84 crash_bytes,
85 } = self;
86 let mut sinks = Vec::<Arc<dyn LogSink>>::with_capacity(2);
87 let mut crash_ring = None;
88 let mut crash_sink = None;
89
90 if matches!(
91 output,
92 LogOutputMode::Terminal | LogOutputMode::TerminalAndFile
93 ) {
94 sinks.push(Arc::new(ConsoleSink::new()));
95 }
96 if matches!(output, LogOutputMode::File | LogOutputMode::TerminalAndFile) {
97 let file = file.ok_or(LogConfigError::MissingFile)?;
98 sinks.push(Arc::new(FileSink::new(file)?));
99 } else if output == LogOutputMode::CrashOnly {
100 let file = file.ok_or(LogConfigError::MissingFile)?;
101 let ring = Arc::new(RingBufferSink::new(crash_events, crash_bytes)?);
102 sinks.push(ring.clone());
103 crash_ring = Some(ring);
104 crash_sink = Some(FileSink::new(file)?);
105 }
106
107 Ok(ConfiguredLogger {
108 dispatcher: LogDispatcher::new(policy, sinks),
109 crash_ring,
110 crash_sink,
111 })
112 }
113}
114
115pub struct ConfiguredLogger {
117 dispatcher: LogDispatcher,
118 crash_ring: Option<Arc<RingBufferSink>>,
119 crash_sink: Option<FileSink>,
120}
121
122impl ConfiguredLogger {
123 pub fn dispatcher(&self) -> &LogDispatcher {
125 &self.dispatcher
126 }
127
128 pub fn dump_crash(&self) -> Result<usize, LogConfigError> {
130 let Some(ring) = &self.crash_ring else {
131 return Ok(0);
132 };
133 let sink = self
134 .crash_sink
135 .as_ref()
136 .ok_or(LogConfigError::MissingFile)?;
137 let events = ring.snapshot();
138 for event in &events {
139 sink.emit(event)?;
140 }
141 Ok(events.len())
142 }
143}