/rust/registry/src/index.crates.io-1949cf8c6b5b557f/logforth-append-file-0.3.0/src/append.rs
Line | Count | Source |
1 | | // Copyright 2024 FastLabs Developers |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | use std::io::Write; |
16 | | use std::num::NonZeroUsize; |
17 | | use std::path::PathBuf; |
18 | | use std::sync::Mutex; |
19 | | use std::sync::MutexGuard; |
20 | | |
21 | | use logforth_core::Diagnostic; |
22 | | use logforth_core::Error; |
23 | | use logforth_core::Layout; |
24 | | use logforth_core::Trap; |
25 | | use logforth_core::append::Append; |
26 | | use logforth_core::layout::PlainTextLayout; |
27 | | use logforth_core::record::Record; |
28 | | |
29 | | use crate::rolling::RollingFileWriter; |
30 | | use crate::rolling::RollingFileWriterBuilder; |
31 | | use crate::rotation::Rotation; |
32 | | |
33 | | /// A builder to configure and create an [`File`] appender. |
34 | | #[derive(Debug)] |
35 | | pub struct FileBuilder { |
36 | | builder: RollingFileWriterBuilder, |
37 | | layout: Box<dyn Layout>, |
38 | | } |
39 | | |
40 | | impl FileBuilder { |
41 | | /// Create a new file appender builder. |
42 | 0 | pub fn new(basedir: impl Into<PathBuf>, filename: impl Into<String>) -> Self { |
43 | 0 | Self { |
44 | 0 | builder: RollingFileWriterBuilder::new(basedir, filename), |
45 | 0 | layout: Box::new(PlainTextLayout::default()), |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | /// Build the [`File`] appender. |
50 | | /// |
51 | | /// # Errors |
52 | | /// |
53 | | /// Return an error if either: |
54 | | /// |
55 | | /// * The log directory cannot be created. |
56 | | /// * The configured filename is empty. |
57 | 0 | pub fn build(self) -> Result<File, Error> { |
58 | 0 | let FileBuilder { builder, layout } = self; |
59 | 0 | let writer = builder.build()?; |
60 | 0 | Ok(File::new(writer, layout)) |
61 | 0 | } |
62 | | |
63 | | /// Set the layout for the logs. |
64 | | /// |
65 | | /// Default to [`PlainTextLayout`]. |
66 | | /// |
67 | | /// # Examples |
68 | | /// |
69 | | /// ``` |
70 | | /// use logforth_append_file::FileBuilder; |
71 | | /// use logforth_layout_json::JsonLayout; |
72 | | /// |
73 | | /// let builder = FileBuilder::new("my_service", "my_app"); |
74 | | /// builder.layout(JsonLayout::default()); |
75 | | /// ``` |
76 | 0 | pub fn layout(mut self, layout: impl Into<Box<dyn Layout>>) -> Self { |
77 | 0 | self.layout = layout.into(); |
78 | 0 | self |
79 | 0 | } |
80 | | |
81 | | /// Set the trap for the file writer. |
82 | | /// |
83 | | /// Default to [`BestEffortTrap`]. |
84 | | /// |
85 | | /// # Examples |
86 | | /// |
87 | | /// ``` |
88 | | /// use logforth_append_file::FileBuilder; |
89 | | /// use logforth_core::trap::BestEffortTrap; |
90 | | /// |
91 | | /// let builder = FileBuilder::new("my_service", "my_app"); |
92 | | /// builder.trap(BestEffortTrap::default()); |
93 | | /// ``` |
94 | | /// |
95 | | /// [`BestEffortTrap`]: logforth_core::trap::BestEffortTrap |
96 | 0 | pub fn trap(mut self, trap: impl Into<Box<dyn Trap>>) -> Self { |
97 | 0 | self.builder = self.builder.trap(trap); |
98 | 0 | self |
99 | 0 | } |
100 | | |
101 | | /// Set the rotation strategy to roll over log files minutely. |
102 | 0 | pub fn rollover_minutely(mut self) -> Self { |
103 | 0 | self.builder = self.builder.rotation(Rotation::Minutely); |
104 | 0 | self |
105 | 0 | } |
106 | | |
107 | | /// Set the rotation strategy to roll over log files hourly. |
108 | 0 | pub fn rollover_hourly(mut self) -> Self { |
109 | 0 | self.builder = self.builder.rotation(Rotation::Hourly); |
110 | 0 | self |
111 | 0 | } |
112 | | |
113 | | /// Set the rotation strategy to roll over log files daily at 00:00 in the local time zone. |
114 | 0 | pub fn rollover_daily(mut self) -> Self { |
115 | 0 | self.builder = self.builder.rotation(Rotation::Daily); |
116 | 0 | self |
117 | 0 | } |
118 | | |
119 | | /// Set the rotation strategy to roll over log files if the current log file exceeds the given |
120 | | /// size. |
121 | | /// |
122 | | /// If any time-based rotation strategy is set, the size-based rotation will be checked on the |
123 | | /// current log file after the time-based rotation check. |
124 | 0 | pub fn rollover_size(mut self, n: NonZeroUsize) -> Self { |
125 | 0 | self.builder = self.builder.max_file_size(n); |
126 | 0 | self |
127 | 0 | } |
128 | | |
129 | | /// Set the filename suffix. |
130 | 0 | pub fn filename_suffix(mut self, suffix: impl Into<String>) -> Self { |
131 | 0 | self.builder = self.builder.filename_suffix(suffix); |
132 | 0 | self |
133 | 0 | } |
134 | | |
135 | | /// Set the maximum number of log files to keep. |
136 | 0 | pub fn max_log_files(mut self, n: NonZeroUsize) -> Self { |
137 | 0 | self.builder = self.builder.max_log_files(n); |
138 | 0 | self |
139 | 0 | } |
140 | | } |
141 | | |
142 | | /// An appender that writes log records to rolling files. |
143 | | #[derive(Debug)] |
144 | | pub struct File { |
145 | | writer: Mutex<RollingFileWriter>, |
146 | | layout: Box<dyn Layout>, |
147 | | } |
148 | | |
149 | | impl File { |
150 | 0 | fn new(writer: RollingFileWriter, layout: Box<dyn Layout>) -> Self { |
151 | 0 | let writer = Mutex::new(writer); |
152 | 0 | Self { writer, layout } |
153 | 0 | } |
154 | | |
155 | 0 | fn writer(&self) -> MutexGuard<'_, RollingFileWriter> { |
156 | 0 | self.writer.lock().unwrap_or_else(|e| e.into_inner()) |
157 | 0 | } |
158 | | } |
159 | | |
160 | | impl Append for File { |
161 | 0 | fn append(&self, record: &Record, diags: &[Box<dyn Diagnostic>]) -> Result<(), Error> { |
162 | 0 | let mut bytes = self.layout.format(record, diags)?; |
163 | 0 | bytes.push(b'\n'); |
164 | 0 | let mut writer = self.writer(); |
165 | 0 | writer.write_all(&bytes).map_err(Error::from_io_error)?; |
166 | 0 | Ok(()) |
167 | 0 | } |
168 | | |
169 | 0 | fn flush(&self) -> Result<(), Error> { |
170 | 0 | let mut writer = self.writer(); |
171 | 0 | writer.flush().map_err(Error::from_io_error)?; |
172 | 0 | Ok(()) |
173 | 0 | } |
174 | | } |
175 | | |
176 | | impl Drop for File { |
177 | 0 | fn drop(&mut self) { |
178 | 0 | let writer = self.writer.get_mut().unwrap_or_else(|e| e.into_inner()); |
179 | 0 | let _ = writer.flush(); |
180 | 0 | } |
181 | | } |