1use std::fs::{File, OpenOptions};
2use std::io::{Result as IoResult, Write};
3
4use chrono::{SecondsFormat, Utc};
5
6use crate::config::Config;
7
8pub struct Logger {
10 file: File
11}
12
13impl Default for Logger {
14 fn default() -> Self {
15 Self::new(&Config::default().log_file.clone()).unwrap()
16 }
17}
18
19impl Logger {
20
21 pub fn new(path: &str) -> IoResult<Self> {
28 let file = OpenOptions::new().create(true).append(true).open(path)?;
29 Ok(Logger { file })
30 }
31
32 pub fn log(&mut self, ip: &str, method: &str, path: &str, status: u16) -> IoResult<()> {
40 let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
41 writeln!(
42 self.file,
43 "[{}] {} \"{} {}\" {}",
44 timestamp, ip, method, path, status
45 )
46 }
47
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use std::fs;
54
55 #[test]
56 fn test_logger_writes_line() {
57 let path = "test_log.log";
58 {
59 let mut logger = Logger::new(path).unwrap();
60 logger.log("127.0.0.1", "GET", "/test", 200).unwrap();
61 }
62 let content = fs::read_to_string(path).unwrap();
63 assert!(content.contains("127.0.0.1"));
64 assert!(content.contains("GET /test"));
65 assert!(content.contains("200"));
66 let _ = fs::remove_file(path);
67 }
68}