logforth_append_file/
append.rs1use std::io::Write;
16use std::num::NonZeroUsize;
17use std::path::PathBuf;
18use std::sync::Mutex;
19use std::sync::MutexGuard;
20
21use logforth_core::Diagnostic;
22use logforth_core::Error;
23use logforth_core::Layout;
24use logforth_core::Trap;
25use logforth_core::append::Append;
26use logforth_core::layout::PlainTextLayout;
27use logforth_core::record::Record;
28
29use crate::rolling::RollingFileWriter;
30use crate::rolling::RollingFileWriterBuilder;
31use crate::rotation::Rotation;
32
33#[derive(Debug)]
37pub struct FileBuilder {
38 builder: RollingFileWriterBuilder,
39 layout: Box<dyn Layout>,
40}
41
42impl FileBuilder {
43 pub fn new(basedir: impl Into<PathBuf>, filename: impl Into<String>) -> Self {
45 Self {
46 builder: RollingFileWriterBuilder::new(basedir, filename),
47 layout: Box::new(PlainTextLayout::default()),
48 }
49 }
50
51 pub fn build(self) -> Result<File, Error> {
60 let FileBuilder { builder, layout } = self;
61 let writer = builder.build()?;
62 Ok(File::new(writer, layout))
63 }
64
65 pub fn layout(mut self, layout: impl Into<Box<dyn Layout>>) -> Self {
79 self.layout = layout.into();
80 self
81 }
82
83 pub fn trap(mut self, trap: impl Into<Box<dyn Trap>>) -> Self {
99 self.builder = self.builder.trap(trap);
100 self
101 }
102
103 pub fn rollover_minutely(mut self) -> Self {
105 self.builder = self.builder.rotation(Rotation::Minutely);
106 self
107 }
108
109 pub fn rollover_hourly(mut self) -> Self {
111 self.builder = self.builder.rotation(Rotation::Hourly);
112 self
113 }
114
115 pub fn rollover_daily(mut self) -> Self {
117 self.builder = self.builder.rotation(Rotation::Daily);
118 self
119 }
120
121 pub fn rollover_size(mut self, n: NonZeroUsize) -> Self {
127 self.builder = self.builder.max_file_size(n);
128 self
129 }
130
131 pub fn filename_suffix(mut self, suffix: impl Into<String>) -> Self {
133 self.builder = self.builder.filename_suffix(suffix);
134 self
135 }
136
137 pub fn max_log_files(mut self, n: NonZeroUsize) -> Self {
139 self.builder = self.builder.max_log_files(n);
140 self
141 }
142}
143
144#[derive(Debug)]
146pub struct File {
147 writer: Mutex<RollingFileWriter>,
148 layout: Box<dyn Layout>,
149}
150
151impl File {
152 fn new(writer: RollingFileWriter, layout: Box<dyn Layout>) -> Self {
153 let writer = Mutex::new(writer);
154 Self { writer, layout }
155 }
156
157 fn writer(&self) -> MutexGuard<'_, RollingFileWriter> {
158 self.writer.lock().unwrap_or_else(|e| e.into_inner())
159 }
160}
161
162impl Append for File {
163 fn append(&self, record: &Record, diags: &[Box<dyn Diagnostic>]) -> Result<(), Error> {
164 let mut bytes = self.layout.format(record, diags)?;
165 bytes.push(b'\n');
166 let mut writer = self.writer();
167 writer.write_all(&bytes).map_err(Error::from_io_error)?;
168 Ok(())
169 }
170
171 fn flush(&self) -> Result<(), Error> {
172 let mut writer = self.writer();
173 writer.flush().map_err(Error::from_io_error)?;
174 Ok(())
175 }
176}
177
178impl Drop for File {
179 fn drop(&mut self) {
180 let writer = self.writer.get_mut().unwrap_or_else(|e| e.into_inner());
181 let _ = writer.flush();
182 }
183}