finchers/server/middleware/
log.rs1use http::{Request, Response};
4use std::sync::Arc;
5
6pub use self::impl_log::{log, LogMiddleware};
7pub use self::impl_stdlog::stdlog;
8
9pub trait Logger {
11 type Instance: Logging;
12
13 fn start<T>(&self, request: &Request<T>) -> Self::Instance;
14}
15
16impl<L: Logger> Logger for Arc<L> {
17 type Instance = L::Instance;
18
19 fn start<T>(&self, request: &Request<T>) -> Self::Instance {
20 (**self).start(request)
21 }
22}
23
24pub trait Logging {
26 fn finish<T>(self, response: &Response<T>);
27}
28
29impl<L: Logging> Logging for Option<L> {
30 fn finish<T>(self, response: &Response<T>) {
31 if let Some(instance) = self {
32 instance.finish(response);
33 }
34 }
35}
36
37mod impl_log {
40 use super::super::{Middleware, Service};
41 use super::{Logger, Logging};
42
43 use futures::{Async, Future, Poll};
44 use http::{Request, Response};
45
46 pub fn log<L>(logger: L) -> LogMiddleware<L>
48 where
49 L: Logger + Clone,
50 {
51 LogMiddleware { logger }
52 }
53
54 #[allow(missing_docs)]
55 #[derive(Debug, Clone)]
56 pub struct LogMiddleware<L> {
57 logger: L,
58 }
59
60 impl<S, L, ReqBody, ResBody> Middleware<S> for LogMiddleware<L>
61 where
62 S: Service<Request = Request<ReqBody>, Response = Response<ResBody>>,
63 L: Logger + Clone,
64 {
65 type Request = Request<ReqBody>;
66 type Response = Response<ResBody>;
67 type Error = S::Error;
68 type Service = LogService<S, L>;
69
70 fn wrap(&self, inner: S) -> Self::Service {
71 LogService {
72 inner,
73 logger: self.logger.clone(),
74 }
75 }
76 }
77
78 #[derive(Debug)]
79 pub struct LogService<S, L> {
80 inner: S,
81 logger: L,
82 }
83
84 impl<S, L, ReqBody, ResBody> Service for LogService<S, L>
85 where
86 S: Service<Request = Request<ReqBody>, Response = Response<ResBody>>,
87 L: Logger,
88 {
89 type Request = Request<ReqBody>;
90 type Response = Response<ResBody>;
91 type Error = S::Error;
92 type Future = LogServiceFuture<S::Future, L::Instance>;
93
94 fn poll_ready(&mut self) -> Poll<(), Self::Error> {
95 self.inner.poll_ready()
96 }
97
98 fn call(&mut self, request: Self::Request) -> Self::Future {
99 let log_session = self.logger.start(&request);
100 LogServiceFuture {
101 future: self.inner.call(request),
102 log_session: Some(log_session),
103 }
104 }
105 }
106
107 #[derive(Debug)]
108 pub struct LogServiceFuture<Fut, L> {
109 future: Fut,
110 log_session: Option<L>,
111 }
112
113 impl<Fut, L, Bd> Future for LogServiceFuture<Fut, L>
114 where
115 Fut: Future<Item = Response<Bd>>,
116 L: Logging,
117 {
118 type Item = Response<Bd>;
119 type Error = Fut::Error;
120
121 fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
122 let response = try_ready!(self.future.poll());
123 let instance = self
124 .log_session
125 .take()
126 .expect("The future has already polled");
127 instance.finish(&response);
128 Ok(Async::Ready(response))
129 }
130 }
131}
132
133mod impl_stdlog {
136 use super::{log, LogMiddleware, Logger, Logging};
137
138 use http::{Method, Request, Response, Uri, Version};
139 use log::{logger, Level, Record};
140 use std::time::Instant;
141
142 pub fn stdlog(level: Level, target: &'static str) -> LogMiddleware<StdLog> {
144 log(StdLog { level, target })
145 }
146
147 #[derive(Debug, Copy, Clone)]
148 pub struct StdLog {
149 level: Level,
150 target: &'static str,
151 }
152
153 impl Logger for StdLog {
154 type Instance = Option<StdLogInstance>;
155
156 fn start<T>(&self, request: &Request<T>) -> Self::Instance {
157 if log_enabled!(target: self.target, self.level) {
158 let start = Instant::now();
159 Some(StdLogInstance {
160 target: self.target,
161 level: self.level,
162 method: request.method().clone(),
163 uri: request.uri().clone(),
164 version: request.version(),
165 start,
166 })
167 } else {
168 None
169 }
170 }
171 }
172
173 #[derive(Debug)]
174 pub struct StdLogInstance {
175 target: &'static str,
176 level: Level,
177 method: Method,
178 uri: Uri,
179 version: Version,
180 start: Instant,
181 }
182
183 impl Logging for StdLogInstance {
184 fn finish<T>(self, response: &Response<T>) {
185 logger().log(
186 &Record::builder()
187 .args(format_args!(
188 "{} {} -> {} ({:?})",
189 self.method,
190 self.uri,
191 response.status(),
192 self.start.elapsed()
193 )).level(self.level)
194 .target(self.target)
195 .build(),
196 );
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use endpoint;
204 use log::Level;
205 use server;
206
207 #[test]
208 #[ignore]
209 fn compiletest_stdlog() {
210 server::start(endpoint::cloned("foo"))
211 .with_middleware(super::stdlog(Level::Debug, "target"))
212 .serve("127.0.0.1:4000")
213 .unwrap();
214 }
215}