Skip to content

Commit e0d3fad

Browse files
committed
feat(logger): impl Logger struct
1 parent 742f135 commit e0d3fad

File tree

6 files changed

+137
-0
lines changed

6 files changed

+137
-0
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ mime_guess = "2.0.3"
4343
rustls = "0.20.2"
4444
rustls-pemfile = "0.2.1"
4545
tokio = { version = "1.14.0", features = ["fs", "rt-multi-thread", "signal", "macros"] }
46+
termcolor = "1.1.2"
4647
tokio-rustls = "0.23.1"
4748
toml = "0.5.8"
4849
serde = { version = "1.0.131", features = ["derive"] }

src/addon/logger.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use anyhow::Result;
2+
use chrono::{DateTime, Utc};
3+
use http::header::USER_AGENT;
4+
use http::Method;
5+
use hyper::Body;
6+
use std::io::Write;
7+
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
8+
9+
use crate::server::middleware::{Request, Response};
10+
11+
pub struct Logger {
12+
buffer_writer: BufferWriter,
13+
}
14+
15+
impl Logger {
16+
pub fn new() -> Self {
17+
let buffer_writer = BufferWriter::stdout(ColorChoice::Always);
18+
19+
Logger { buffer_writer }
20+
}
21+
22+
pub async fn log(&mut self, request: Request<Body>, response: Response<Body>) -> Result<()> {
23+
let mut buffer = self.buffer_writer.buffer();
24+
let request = request.lock().await;
25+
let response = response.lock().await;
26+
27+
// UTC Time
28+
let moment: DateTime<Utc> = Utc::now();
29+
write!(&mut buffer, "[{:?}] \"", moment)?;
30+
31+
// HTTP Request Method
32+
let method = request.method();
33+
34+
match *method {
35+
Method::GET => buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?,
36+
Method::POST | Method::PUT | Method::PATCH => {
37+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?
38+
}
39+
Method::DELETE => buffer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?,
40+
_ => buffer.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)))?,
41+
};
42+
43+
write!(&mut buffer, "{} ", method)?;
44+
buffer.reset()?;
45+
46+
// HTTP Request URI and Version
47+
write!(&mut buffer, "{} {:?} ", request.uri(), request.version())?;
48+
49+
// HTTP Response Status Code
50+
match response.status().as_u16() {
51+
100..=199 => {
52+
// Informational Responses
53+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?;
54+
}
55+
200..=299 => {
56+
// Successful responses
57+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
58+
}
59+
300..=399 => {
60+
// Redirection messages
61+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?;
62+
}
63+
400..=499 => {
64+
// Client error responses
65+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Rgb(255, 140, 0))))?;
66+
}
67+
500..=599 => {
68+
// Server error responses
69+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
70+
}
71+
_ => {
72+
// Unknown response codes
73+
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)))?;
74+
}
75+
}
76+
write!(&mut buffer, "{}", response.status())?;
77+
buffer.reset()?;
78+
79+
// HTTP Request User Agent
80+
let user_agent = if let Some(value) = request.headers().get(USER_AGENT) {
81+
value.to_str()?
82+
} else {
83+
"N/A"
84+
};
85+
write!(&mut buffer, "\" \"{}\" ", user_agent)?;
86+
87+
writeln!(&mut buffer)?;
88+
self.buffer_writer.print(&buffer)?;
89+
buffer.reset()?;
90+
91+
Ok(())
92+
}
93+
}

src/addon/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod compression;
22
pub mod cors;
33
pub mod file_server;
4+
pub mod logger;

src/server/middleware/logger.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use http::{Request, Response};
2+
use hyper::Body;
3+
use std::sync::Arc;
4+
use tokio::sync::Mutex;
5+
6+
use crate::addon::logger::Logger;
7+
8+
use super::MiddlewareAfter;
9+
10+
pub fn make_logger_middleware() -> MiddlewareAfter {
11+
let logger = Arc::new(Mutex::new(Logger::new()));
12+
13+
Box::new(
14+
move |request: Arc<Mutex<Request<Body>>>, response: Arc<Mutex<Response<Body>>>| {
15+
let logger = Arc::clone(&logger);
16+
17+
Box::pin(async move {
18+
let mut logger = logger.lock().await;
19+
20+
if let Err(error) = logger.log(request, response).await {
21+
eprintln!("{:#?}", error);
22+
}
23+
24+
Ok(())
25+
})
26+
},
27+
)
28+
}

src/server/middleware/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod basic_auth;
22
pub mod cors;
33
pub mod gzip;
4+
pub mod logger;
45

56
use anyhow::Error;
67
use futures::Future;
@@ -16,6 +17,7 @@ use crate::config::Config;
1617
use self::basic_auth::make_basic_auth_middleware;
1718
use self::cors::make_cors_middleware;
1819
use self::gzip::make_gzip_compression_middleware;
20+
use self::logger::make_logger_middleware;
1921

2022
/// Middleware HTTP Response which expands to a `Arc<Mutex<http::Request<T>>>`
2123
pub type Request<T> = Arc<Mutex<http::Request<T>>>;
@@ -119,6 +121,8 @@ impl TryFrom<Arc<Config>> for Middleware {
119121
}
120122
}
121123

124+
middleware.after(make_logger_middleware());
125+
122126
Ok(middleware)
123127
}
124128
}

0 commit comments

Comments
 (0)