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::append::Append;
25use logforth_core::layout::PlainTextLayout;
26use logforth_core::record::Record;
27
28use crate::rolling::RollingFileWriter;
29use crate::rolling::RollingFileWriterBuilder;
30use crate::rotation::Rotation;
31
32#[derive(Debug)]
34pub struct FileBuilder {
35 builder: RollingFileWriterBuilder,
36 layout: Box<dyn Layout>,
37}
38
39impl FileBuilder {
40 pub fn new(basedir: impl Into<PathBuf>, filename: impl Into<String>) -> Self {
42 Self {
43 builder: RollingFileWriterBuilder::new(basedir, filename),
44 layout: Box::new(PlainTextLayout::default()),
45 }
46 }
47
48 pub fn build(self) -> Result<File, Error> {
57 let FileBuilder { builder, layout } = self;
58 let writer = builder.build()?;
59 Ok(File::new(writer, layout))
60 }
61
62 pub fn layout(mut self, layout: impl Into<Box<dyn Layout>>) -> Self {
76 self.layout = layout.into();
77 self
78 }
79
80 pub fn rollover_minutely(mut self) -> Self {
82 self.builder = self.builder.rotation(Rotation::Minutely);
83 self
84 }
85
86 pub fn rollover_hourly(mut self) -> Self {
88 self.builder = self.builder.rotation(Rotation::Hourly);
89 self
90 }
91
92 pub fn rollover_daily(mut self) -> Self {
94 self.builder = self.builder.rotation(Rotation::Daily);
95 self
96 }
97
98 pub fn rollover_size(mut self, n: NonZeroUsize) -> Self {
104 self.builder = self.builder.max_file_size(n);
105 self
106 }
107
108 pub fn filename_suffix(mut self, suffix: impl Into<String>) -> Self {
110 self.builder = self.builder.filename_suffix(suffix);
111 self
112 }
113
114 pub fn max_log_files(mut self, n: NonZeroUsize) -> Self {
116 self.builder = self.builder.max_log_files(n);
117 self
118 }
119}
120
121#[derive(Debug)]
123pub struct File {
124 writer: Mutex<RollingFileWriter>,
125 layout: Box<dyn Layout>,
126}
127
128impl File {
129 fn new(writer: RollingFileWriter, layout: Box<dyn Layout>) -> Self {
130 let writer = Mutex::new(writer);
131 Self { writer, layout }
132 }
133
134 fn writer(&self) -> MutexGuard<'_, RollingFileWriter> {
135 self.writer.lock().unwrap_or_else(|e| e.into_inner())
136 }
137}
138
139impl Append for File {
140 fn append(&self, record: &Record, diags: &[Box<dyn Diagnostic>]) -> Result<(), Error> {
141 let mut bytes = self.layout.format(record, diags)?;
142 bytes.push(b'\n');
143 let mut writer = self.writer();
144 writer.write_all(&bytes).map_err(Error::from_io_error)?;
145 Ok(())
146 }
147
148 fn flush(&self) -> Result<(), Error> {
149 let mut writer = self.writer();
150 writer.flush().map_err(Error::from_io_error)?;
151 Ok(())
152 }
153}