conduit_log_requests/
lib.rs1#![cfg_attr(test, deny(warnings))]
2
3#[macro_use] extern crate log;
4
5extern crate time;
6extern crate conduit;
7extern crate conduit_middleware as middleware;
8
9use std::error::Error;
10
11use conduit::{Request, Response};
12use middleware::Middleware;
13
14pub struct LogRequests(pub log::LogLevel);
15
16struct LogStart(u64);
17
18impl Middleware for LogRequests {
19 fn before(&self, req: &mut Request) -> Result<(), Box<Error+Send>> {
20 req.mut_extensions().insert(LogStart(time::precise_time_ns()));
21 Ok(())
22 }
23
24 fn after(&self, req: &mut Request, resp: Result<Response, Box<Error+Send>>)
25 -> Result<Response, Box<Error+Send>> {
26 let LogStart(start) = *req.mut_extensions().find::<LogStart>().unwrap();
27
28 match resp {
29 Ok(ref resp) => self.log_message(req, start, resp.status.0, None),
30 Err(ref e) => {
31 let msg: &Error = &**e;
32 self.log_message(req, start, 500, Some(msg))
33 }
34 }
35 resp
36 }
37
38}
39
40impl LogRequests {
41 fn log_message(&self, req: &mut Request, start: u64, status: u32,
42 msg: Option<&Error>) {
43 let LogRequests(level) = *self;
44 let level = if msg.is_some() {log::LogLevel::Error} else {level};
45 log!(level, "{} [{}] {:?} {} - {}ms {}{}",
46 req.remote_addr(),
47 time::now().rfc3339(),
48 req.method(),
49 req.path(),
50 (time::precise_time_ns() - start) / 1000000,
51 status,
52 match msg {
53 None => String::new(),
54 Some(s) => format!(": {} {}", s.description(), s),
55 })
56 }
57}
58
59#[cfg(all(test, foo))] mod tests {
61 extern crate conduit_test as test;
62
63 use {LogRequests};
64
65 use conduit::{Request, Response, Handler, Method};
66 use log::{Log, LogRecord};
67 use log;
68 use middleware;
69 use std::error::Error;
70 use std::old_io::{ChanWriter, ChanReader};
71 use std::sync::Mutex;
72 use std::sync::mpsc::{channel, Sender};
73 use std::thread::Thread;
74 use std;
75
76 struct MyWriter(Mutex<ChanWriter>);
77
78 impl Log for MyWriter {
79 fn enabled(&self, _: log::LogLevel, _: &str) -> bool { true }
80 fn log(&self, record: &LogRecord) {
81 let MyWriter(ref inner) = *self;
82 (write!(inner.lock(), "{}", record.args)).unwrap();
83 }
84 }
85
86 #[test]
87 fn test_log() {
88 let (sender, receiver) = channel();
89 let mut reader = ChanReader::new(receiver);
90
91 let mut builder = middleware::MiddlewareBuilder::new(handler);
92 builder.add(LogRequests(log::LogLevel::Error));
93
94 task(builder, sender);
95
96 let result = reader.read_to_string().ok().expect("No response");
97 let parts = result.as_slice().split(' ').map(|s| s.to_string()).collect::<Vec<String>>();
98
99 assert_eq!(parts[0].as_slice(), "127.0.0.1");
100 assert_eq!(parts[2].as_slice(), "Get");
104 assert_eq!(parts[3].as_slice(), "/foo");
105 }
106
107 fn task<H: Handler + 'static + Send>(handler: H, sender: Sender<Vec<u8>>) {
108 Thread::spawn(move|| {
109 log::set_logger(Box::new(MyWriter(Mutex::new(ChanWriter::new(sender)))));
110 let mut request = test::MockRequest::new(Method::Get, "/foo");
111 let _ = handler.call(&mut request);
112 });
113 }
114
115 fn handler(_: &mut Request) -> Result<Response, Box<Error+Send>> {
116 Ok(Response {
117 status: (200, "OK"),
118 headers: std::collections::HashMap::new(),
119 body: Box::new(std::old_io::util::NullReader)
120 })
121 }
122}