tower_web/middleware/log/
service.rs1use futures::{Future, Poll};
2use http;
3use log::{logger, Level, Record};
4use tower_service::Service;
5
6use std::time::Instant;
7
8#[derive(Debug)]
10pub struct LogService<S> {
11 inner: S,
12 target: &'static str,
13}
14
15#[derive(Debug)]
17pub struct ResponseFuture<T> {
18 inner: T,
19 context: Option<LogContext>,
20}
21
22#[derive(Debug)]
23struct LogContext {
24 method: http::Method,
25 path: Option<http::uri::PathAndQuery>,
26 version: http::Version,
27 start: Instant,
28 target: &'static str,
29}
30
31impl<S> LogService<S> {
32 pub(super) fn new(inner: S, target: &'static str) -> LogService<S> {
33 LogService {
34 inner,
35 target,
36 }
37 }
38}
39
40impl<S, RequestBody, ResponseBody> Service for LogService<S>
41where S: Service<Request = http::Request<RequestBody>,
42 Response = http::Response<ResponseBody>>,
43 S::Error: ::std::error::Error,
44{
45 type Request = S::Request;
46 type Response = S::Response;
47 type Error = S::Error;
48 type Future = ResponseFuture<S::Future>;
49
50 fn poll_ready(&mut self) -> Poll<(), Self::Error> {
51 self.inner.poll_ready()
52 }
53
54 fn call(&mut self, request: Self::Request) -> Self::Future {
55 let context = if log_enabled!(target: self.target, Level::Info) {
56 Some(LogContext {
57 method: request.method().clone(),
58 path: request.uri().path_and_query().map(|p| p.clone()),
59 version: request.version(),
60 start: Instant::now(),
61 target: self.target,
62 })
63 } else {
64 None
65 };
66
67 let inner = self.inner.call(request);
68
69 ResponseFuture {
70 inner,
71 context,
72 }
73 }
74}
75
76impl<T, B> Future for ResponseFuture<T>
77where
78 T: Future<Item = http::Response<B>>,
79 T::Error: ::std::error::Error,
80{
81 type Item = T::Item;
82 type Error = T::Error;
83
84 fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
85 use futures::Async::*;
86
87 let res = self.inner.poll();
88
89 match (&res, &self.context) {
90 (Ok(Ready(ref response)), Some(ref context)) => {
91 let full_path = context.path.as_ref()
92 .map(|p| p.as_str())
93 .unwrap_or("/");
94
95 let status_code = response.status().as_u16();
100 let level = match status_code {
101 400...599 => Level::Error,
102 _ => Level::Info,
103 };
104 logger().log(&Record::builder()
105 .args(format_args!(
106 "\"{} {} {:?}\" {} {:?}",
107 context.method,
108 full_path,
109 context.version,
110 status_code,
111 context.start.elapsed(),
112 ))
113 .level(level)
114 .target(context.target)
115 .module_path(Some(module_path!()))
116 .file(Some(file!()))
117 .line(Some(line!()))
118 .build());
119 }
120 (Err(ref err), ..) => {
121 warn!("ERROR: {}", err);
122 }
123 _ => {}
124 }
125
126 res
127 }
128}