4
4
use super :: prelude:: * ;
5
5
use crate :: util:: request_header;
6
6
use conduit:: Request ;
7
- use std:: fmt;
7
+ use std:: fmt:: { self , Display , Formatter } ;
8
8
use std:: time:: Instant ;
9
9
10
+ const SLOW_REQUEST_THRESHOLD_MS : u64 = 1000 ;
11
+
10
12
#[ allow( missing_debug_implementations) ] // We can't
11
13
#[ derive( Default ) ]
12
14
pub struct LogRequests {
@@ -23,61 +25,129 @@ impl Handler for LogRequests {
23
25
fn call ( & self , req : & mut dyn Request ) -> Result < Response > {
24
26
let request_start = Instant :: now ( ) ;
25
27
let res = self . handler . as_ref ( ) . unwrap ( ) . call ( req) ;
26
- let ( level, response_code) = match res {
27
- Ok ( ref r) => ( "info" , r. status . 0 ) ,
28
- Err ( _) => ( "error" , 500 ) ,
29
- } ;
30
28
let response_time = request_start. elapsed ( ) ;
31
29
let response_time =
32
30
response_time. as_secs ( ) * 1000 + u64:: from ( response_time. subsec_nanos ( ) ) / 1_000_000 ;
33
31
34
- let metadata_length = req
35
- . extensions ( )
36
- . find :: < u64 > ( )
37
- . map_or ( String :: new ( ) , |l| format ! ( " metadata_length={}" , l) ) ;
32
+ println ! (
33
+ "{}" ,
34
+ RequestLine {
35
+ req,
36
+ res: & res,
37
+ response_time,
38
+ }
39
+ ) ;
38
40
39
- let slow_request = if response_time > 1000 {
40
- " SLOW REQUEST"
41
- } else {
42
- ""
43
- } ;
41
+ res
42
+ }
43
+ }
44
+
45
+ struct RequestLine < ' r > {
46
+ req : & ' r dyn Request ,
47
+ res : & ' r Result < Response > ,
48
+ response_time : u64 ,
49
+ }
50
+
51
+ impl Display for RequestLine < ' _ > {
52
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
53
+ let mut line = LogLine :: new ( f) ;
44
54
45
- let error = if let Err ( ref e) = res {
46
- format ! ( " error=\" {}\" " , e)
47
- } else {
48
- String :: new ( )
55
+ let ( at, status) = match self . res {
56
+ Ok ( resp) => ( "info" , resp. status . 0 ) ,
57
+ Err ( _) => ( "error" , 500 ) ,
49
58
} ;
50
59
51
- println ! (
52
- "at={level} method={method} path=\" {path}\" \
53
- request_id={request_id} fwd=\" {ip}\" service={time_ms}ms \
54
- status={status} user_agent=\" {user_agent}\" \
55
- {metadata_length}{error}{slow_request}",
56
- level = level,
57
- method = req. method( ) ,
58
- path = FullPath ( req) ,
59
- ip = request_header( req, "X-Real-Ip" ) ,
60
- time_ms = response_time,
61
- user_agent = request_header( req, "User-Agent" ) ,
62
- request_id = request_header( req, "X-Request-Id" ) ,
63
- status = response_code,
64
- metadata_length = metadata_length,
65
- error = error,
66
- slow_request = slow_request,
67
- ) ;
60
+ line. add_field ( "at" , at) ?;
61
+ line. add_field ( "method" , self . req . method ( ) ) ?;
62
+ line. add_quoted_field ( "path" , FullPath ( self . req ) ) ?;
63
+ line. add_field ( "request_id" , request_header ( self . req , "X-Request-Id" ) ) ?;
64
+ line. add_quoted_field ( "fwd" , request_header ( self . req , "X-Real-Ip" ) ) ?;
65
+ line. add_field ( "service" , TimeMs ( self . response_time ) ) ?;
66
+ line. add_field ( "status" , status) ?;
67
+ line. add_quoted_field ( "user_agent" , request_header ( self . req , "User-Agent" ) ) ?;
68
68
69
- res
69
+ if let Some ( len) = self . req . extensions ( ) . find :: < u64 > ( ) {
70
+ line. add_field ( "metadata_length" , len) ?;
71
+ }
72
+
73
+ if let Err ( err) = self . res {
74
+ line. add_quoted_field ( "error" , err) ?;
75
+ }
76
+
77
+ if self . response_time > SLOW_REQUEST_THRESHOLD_MS {
78
+ line. add_marker ( "SLOW REQUEST" ) ?;
79
+ }
80
+
81
+ Ok ( ( ) )
70
82
}
71
83
}
72
84
73
85
struct FullPath < ' a > ( & ' a dyn Request ) ;
74
86
75
- impl < ' a > fmt :: Display for FullPath < ' a > {
76
- fn fmt ( & self , f : & mut fmt :: Formatter < ' _ > ) -> fmt:: Result {
87
+ impl < ' a > Display for FullPath < ' a > {
88
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
77
89
write ! ( f, "{}" , self . 0 . path( ) ) ?;
78
90
if let Some ( q_string) = self . 0 . query_string ( ) {
79
91
write ! ( f, "?{}" , q_string) ?;
80
92
}
81
93
Ok ( ( ) )
82
94
}
83
95
}
96
+
97
+ struct TimeMs ( u64 ) ;
98
+
99
+ impl Display for TimeMs {
100
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
101
+ self . 0 . fmt ( f) ?;
102
+ f. write_str ( "ms" ) ?;
103
+ Ok ( ( ) )
104
+ }
105
+ }
106
+
107
+ struct LogLine < ' f , ' g > {
108
+ f : & ' f mut Formatter < ' g > ,
109
+ first : bool ,
110
+ }
111
+
112
+ impl < ' f , ' g > LogLine < ' f , ' g > {
113
+ fn new ( f : & ' f mut Formatter < ' g > ) -> Self {
114
+ Self { f, first : true }
115
+ }
116
+
117
+ fn add_field < K : Display , V : Display > ( & mut self , key : K , value : V ) -> fmt:: Result {
118
+ self . start_item ( ) ?;
119
+
120
+ key. fmt ( self . f ) ?;
121
+ self . f . write_str ( "=" ) ?;
122
+ value. fmt ( self . f ) ?;
123
+
124
+ Ok ( ( ) )
125
+ }
126
+
127
+ fn add_quoted_field < K : Display , V : Display > ( & mut self , key : K , value : V ) -> fmt:: Result {
128
+ self . start_item ( ) ?;
129
+
130
+ key. fmt ( self . f ) ?;
131
+ self . f . write_str ( "=\" " ) ?;
132
+ value. fmt ( self . f ) ?;
133
+ self . f . write_str ( "\" " ) ?;
134
+
135
+ Ok ( ( ) )
136
+ }
137
+
138
+ fn add_marker < M : Display > ( & mut self , marker : M ) -> fmt:: Result {
139
+ self . start_item ( ) ?;
140
+
141
+ marker. fmt ( self . f ) ?;
142
+
143
+ Ok ( ( ) )
144
+ }
145
+
146
+ fn start_item ( & mut self ) -> fmt:: Result {
147
+ if !self . first {
148
+ self . f . write_str ( " " ) ?;
149
+ }
150
+ self . first = false ;
151
+ Ok ( ( ) )
152
+ }
153
+ }
0 commit comments