1extern crate async_logger;
91extern crate log;
92
93#[cfg(feature="time")]
94extern crate time;
95
96
97use log::{Log, Metadata, Record};
98use async_logger::{AsyncLoggerNB, FileWriter, Error};
99use std::sync::Arc;
100
101#[cfg(feature="time")]
102use time::OffsetDateTime;
103
104
105const DEFAULT_BUF_SIZE: usize = 256;
106const DEFAULT_LOG_FILE_SIZE: usize = 10*1024*1024;
107
108
109pub struct Logger {
111 async_logger: Arc<AsyncLoggerNB<Box<String>>>,
112 formatter: fn(&Record) -> String,
113}
114
115impl Logger {
116
117 pub fn new(log_dir: &str, buf_sz: usize, file_size: usize) -> Result<Logger, Error> {
123
124 let writer = FileWriter::new(log_dir, file_size)?;
125
126 let async_logger = Arc::new(AsyncLoggerNB::new(Box::new(writer), buf_sz)?);
127
128 let formatter = Logger::format_msg;
129
130 Ok(Logger {
131 async_logger,
132 formatter,
133 })
134 }
135
136 pub fn builder() -> LoggerBuilder {
138 LoggerBuilder {
139 buf_sz: None,
140 writer: None,
141 formatter: None,
142 }
143 }
144
145 fn format_msg(record: &Record) -> String {
146
147 let time;
148 #[cfg(feature="time")]
149 {
150 time = OffsetDateTime::now_local().format("%Y-%m-%d %H:%M:%S.%N%z");
151 }
152 #[cfg(not(feature="time"))]
153 {
154 time = std::time::SystemTime::now()
155 .duration_since(std::time::UNIX_EPOCH)
156 .unwrap_or(std::time::Duration::new(0,0))
157 .as_secs();
158 }
159
160 format!("[{} {} {}]: {}\n", time, record.level(), record.target(), record.args())
161 }
162}
163
164impl Log for Logger {
165
166 fn enabled(&self, metadata: &Metadata) -> bool {
167
168 return metadata.level() <= log::max_level();
169 }
170
171 fn log(&self, record: &Record) {
172
173 let msg = (self.formatter)(record);
174
175 let _ = self.async_logger.write_value(Box::new(msg));
176 }
177
178 fn flush(&self) {
179
180 AsyncLoggerNB::flush(&self.async_logger);
181 }
182}
183
184pub struct LoggerBuilder {
187 buf_sz: Option<usize>,
188 writer: Option<Box<dyn async_logger::Writer<Box<String>>>>,
189 formatter: Option<fn(&Record) -> String>,
190}
191
192impl LoggerBuilder {
193
194 pub fn buf_size(mut self, size: usize) -> Self {
196 self.buf_sz = Some(size);
197 self
198 }
199
200 pub fn formatter(mut self, formatter: fn(&Record) -> String) -> Self {
202 self.formatter = Some(formatter);
203 self
204 }
205
206 pub fn writer(mut self, writer: Box<dyn async_logger::Writer<Box<String>>>) -> Self {
208 self.writer = Some(writer);
209 self
210 }
211
212 pub fn build(self) -> Result<Logger,Error> {
214
215 let buf_sz = match self.buf_sz {
216 Some(buf_sz) => buf_sz,
217 None => DEFAULT_BUF_SIZE,
218 };
219
220 let writer = match self.writer {
221 Some(writer) => writer,
222 None => Box::new(FileWriter::new(".", DEFAULT_LOG_FILE_SIZE)?),
223 };
224
225 let formatter = match self.formatter {
226 Some(formatter) => formatter,
227 None => Logger::format_msg,
228 };
229
230 let async_logger = Arc::new(AsyncLoggerNB::new(writer, buf_sz)?);
231
232 Ok(Logger {
233 async_logger,
234 formatter,
235 })
236 }
237}
238
239#[cfg(test)]
240mod tests {
241
242 use super::*;
243 use async_logger::ErrorKind;
244 use std::path::Path;
245
246
247 const LOG_DIR: &str = "/tmp/AsyncLoggerNBTest_000239400377";
248 const NONEXISTING_LOG_DIR: &str = "/tmp/AsyncLoggerNBTest_85003857407";
249 const LOG_FILE_SIZE: usize = 4096;
250
251 #[test]
252 fn test_error() {
253
254 if Path::new(LOG_DIR).exists() {
257 std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
258 }
259
260 std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
261
262 match Logger::new(LOG_DIR, 0, LOG_FILE_SIZE) {
263 Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
264 _ => panic!("Expected error, got Ok!"),
265 }
266
267 std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
268 std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
269
270 match Logger::new(LOG_DIR, std::usize::MAX, LOG_FILE_SIZE) {
271 Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
272 _ => panic!("Expected error, got Ok!"),
273 }
274
275 std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
276
277 match Logger::new(NONEXISTING_LOG_DIR, 100, LOG_FILE_SIZE) {
278 Err(e) if e.kind() == ErrorKind::IoError => {
279 },
280 _ => panic!("Expected error, got Ok!"),
281 }
282
283 std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
286
287 let writer = FileWriter::new(LOG_DIR, LOG_FILE_SIZE).expect("Failed to create file writer");
288
289 match Logger::builder()
290 .buf_size(0)
291 .writer(Box::new(writer))
292 .build()
293 {
294 Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
295 _ => panic!("Expected error, got Ok!"),
296 }
297
298 std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
299 std::fs::create_dir(LOG_DIR).expect("Failed to create test dir");
300
301 let writer = FileWriter::new(LOG_DIR, LOG_FILE_SIZE).expect("Failed to create file writer");
302
303 match Logger::builder()
304 .buf_size(std::usize::MAX)
305 .writer(Box::new(writer))
306 .build()
307 {
308 Err(e) if e.kind() == ErrorKind::IncorrectBufferSize => {},
309 _ => panic!("Expected error, got Ok!"),
310 }
311
312 std::fs::remove_dir_all(LOG_DIR).expect("Failed to delete test dir on cleanup");
313 }
314}