use std::fs::{File, OpenOptions};
use std::io::{self, prelude::*};
use std::path::Path;
use std::sync::Mutex;
use crate::{extension::RealIp, HeaderType, Middleware, Request, Response};
#[derive(Debug)]
pub enum Level {
Debug,
Info,
}
#[derive(Debug)]
pub struct Logger {
level: Level,
real_ip: Option<HeaderType>,
file: Option<Mutex<File>>,
console: bool,
}
impl Logger {
pub fn new() -> Logger {
Logger {
level: Level::Info,
real_ip: None,
file: None,
console: true,
}
}
pub fn level(self, level: Level) -> Self {
Self { level, ..self }
}
pub fn real_ip(self, real_ip: HeaderType) -> Self {
Self {
real_ip: Some(real_ip),
..self
}
}
pub fn file(self, file: impl AsRef<Path>) -> io::Result<Self> {
Ok(Self {
file: Some(Mutex::new(
OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(file)?,
)),
..self
})
}
pub fn console(self, console: bool) -> Self {
Self { console, ..self }
}
fn log(&self, req: &Request) {
let ip = match &self.real_ip {
Some(i) => req.real_ip_header(i),
None => req.address.ip(),
};
match self.level {
Level::Debug => {
let mut headers = "".to_string();
for i in &*req.headers {
headers += &format!("{}: {}, ", i.name, i.value);
}
if headers.len() >= 2 {
headers = headers[0..headers.len() - 2].to_string()
}
let mut query = "".to_string();
for i in req.query.iter() {
query += &format!("{}: {}, ", i[0], i[1]);
}
if query.len() >= 2 {
query = query[0..query.len() - 2].to_string()
}
let mut new_path = req.path.to_owned();
if new_path.is_empty() {
new_path = "/".to_string();
}
self.send_log(format!(
"[{ip}] {} {} [{}] ({}) {{{}}}",
req.method,
new_path,
query,
headers,
String::from_utf8_lossy(&req.body).replace('\n', "\\n")
))
}
Level::Info => {
let mut new_path = req.path.clone();
if new_path.is_empty() {
new_path = "/".to_string();
}
self.send_log(format!("[{ip}] {} {}{}", req.method, new_path, req.query))
}
}
}
fn send_log(&self, data: String) {
if self.console {
println!("{data}");
}
if let Some(i) = &self.file {
if let Err(e) = writeln!(i.lock().unwrap(), "{data}") {
eprintln!("[-] Erm... Error writhing to log file: {e}")
}
}
}
}
impl Middleware for Logger {
fn end(&self, req: &Request, _res: &Response) {
self.log(req);
}
}
impl Default for Logger {
fn default() -> Logger {
Logger::new()
}
}