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)]
35pub struct FileBuilder {
36 builder: RollingFileWriterBuilder,
37 layout: Box<dyn Layout>,
38}
39
40impl FileBuilder {
41 pub fn new(basedir: impl Into<PathBuf>, filename: impl Into<String>) -> Self {
43 Self {
44 builder: RollingFileWriterBuilder::new(basedir, filename),
45 layout: Box::new(PlainTextLayout::default()),
46 }
47 }
48
49 pub fn build(self) -> Result<File, Error> {
58 let FileBuilder { builder, layout } = self;
59 let writer = builder.build()?;
60 Ok(File::new(writer, layout))
61 }
62
63 pub fn layout(mut self, layout: impl Into<Box<dyn Layout>>) -> Self {
77 self.layout = layout.into();
78 self
79 }
80
81 pub fn trap(mut self, trap: impl Into<Box<dyn Trap>>) -> Self {
95 self.builder = self.builder.trap(trap);
96 self
97 }
98
99 pub fn rollover_minutely(mut self) -> Self {
101 self.builder = self.builder.rotation(Rotation::Minutely);
102 self
103 }
104
105 pub fn rollover_hourly(mut self) -> Self {
107 self.builder = self.builder.rotation(Rotation::Hourly);
108 self
109 }
110
111 pub fn rollover_daily(mut self) -> Self {
113 self.builder = self.builder.rotation(Rotation::Daily);
114 self
115 }
116
117 pub fn rollover_size(mut self, n: NonZeroUsize) -> Self {
123 self.builder = self.builder.max_file_size(n);
124 self
125 }
126
127 pub fn filename_suffix(mut self, suffix: impl Into<String>) -> Self {
129 self.builder = self.builder.filename_suffix(suffix);
130 self
131 }
132
133 pub fn max_log_files(mut self, n: NonZeroUsize) -> Self {
135 self.builder = self.builder.max_log_files(n);
136 self
137 }
138}
139
140#[derive(Debug)]
142pub struct File {
143 writer: Mutex<RollingFileWriter>,
144 layout: Box<dyn Layout>,
145}
146
147impl File {
148 fn new(writer: RollingFileWriter, layout: Box<dyn Layout>) -> Self {
149 let writer = Mutex::new(writer);
150 Self { writer, layout }
151 }
152
153 fn writer(&self) -> MutexGuard<'_, RollingFileWriter> {
154 self.writer.lock().unwrap_or_else(|e| e.into_inner())
155 }
156}
157
158impl Append for File {
159 fn append(&self, record: &Record, diags: &[Box<dyn Diagnostic>]) -> Result<(), Error> {
160 let mut bytes = self.layout.format(record, diags)?;
161 bytes.push(b'\n');
162 let mut writer = self.writer();
163 writer.write_all(&bytes).map_err(Error::from_io_error)?;
164 Ok(())
165 }
166
167 fn flush(&self) -> Result<(), Error> {
168 let mut writer = self.writer();
169 writer.flush().map_err(Error::from_io_error)?;
170 Ok(())
171 }
172}
173
174impl Drop for File {
175 fn drop(&mut self) {
176 let writer = self.writer.get_mut().unwrap_or_else(|e| e.into_inner());
177 let _ = writer.flush();
178 }
179}