@@ -7,8 +7,16 @@ use conduit::Request;
7
7
use std:: fmt:: { self , Display , Formatter } ;
8
8
use std:: time:: Instant ;
9
9
10
+ const LOG_METADATA_HEADER_PREFIX : & str = "X-CratesIo-Log-Metadata-" ;
10
11
const SLOW_REQUEST_THRESHOLD_MS : u64 = 1000 ;
11
12
13
+ pub fn add_custom_metadata < K : Display , V : Display > ( resp : & mut Response , key : K , value : V ) {
14
+ resp. headers . insert (
15
+ format ! ( "{}{}" , LOG_METADATA_HEADER_PREFIX , key) ,
16
+ vec ! [ value. to_string( ) ] ,
17
+ ) ;
18
+ }
19
+
12
20
#[ allow( missing_debug_implementations) ] // We can't
13
21
#[ derive( Default ) ]
14
22
pub struct LogRequests {
@@ -24,17 +32,45 @@ impl AroundMiddleware for LogRequests {
24
32
impl Handler for LogRequests {
25
33
fn call ( & self , req : & mut dyn Request ) -> Result < Response > {
26
34
let request_start = Instant :: now ( ) ;
27
- let res = self . handler . as_ref ( ) . unwrap ( ) . call ( req) ;
35
+ let mut res = self . handler . as_ref ( ) . unwrap ( ) . call ( req) ;
28
36
let response_time = request_start. elapsed ( ) ;
29
37
let response_time =
30
38
response_time. as_secs ( ) * 1000 + u64:: from ( response_time. subsec_nanos ( ) ) / 1_000_000 ;
31
39
40
+ // To aid debugging issues in production, some routes attach extra metadata to the log
41
+ // line. An example of this is the crate publishing route, which adds the published crate
42
+ // name and version to the line.
43
+ //
44
+ // This is implemented as special HTTP headers:
45
+ //
46
+ // X-CratesIo-Log-Metadata-{key}: {value}
47
+ //
48
+ // When this middleware finds those headers in the response, it removes them (so the user
49
+ // can't see them) and adds the key/value pair at the end of the log line.
50
+ let mut custom_metadata = Vec :: new ( ) ;
51
+ if let Ok ( resp) = & mut res {
52
+ resp. headers . retain ( |key, value| {
53
+ if key. starts_with ( LOG_METADATA_HEADER_PREFIX ) {
54
+ if let Some ( value) = value. get ( 0 ) {
55
+ custom_metadata. push ( (
56
+ key[ LOG_METADATA_HEADER_PREFIX . len ( ) ..] . to_string ( ) ,
57
+ value. clone ( ) ,
58
+ ) ) ;
59
+ }
60
+ false
61
+ } else {
62
+ true
63
+ }
64
+ } ) ;
65
+ }
66
+
32
67
println ! (
33
68
"{}" ,
34
69
RequestLine {
35
70
req,
36
71
res: & res,
37
72
response_time,
73
+ custom_metadata,
38
74
}
39
75
) ;
40
76
@@ -45,6 +81,7 @@ impl Handler for LogRequests {
45
81
struct RequestLine < ' r > {
46
82
req : & ' r dyn Request ,
47
83
res : & ' r Result < Response > ,
84
+ custom_metadata : Vec < ( String , String ) > ,
48
85
response_time : u64 ,
49
86
}
50
87
@@ -66,6 +103,10 @@ impl Display for RequestLine<'_> {
66
103
line. add_field ( "status" , status) ?;
67
104
line. add_quoted_field ( "user_agent" , request_header ( self . req , "User-Agent" ) ) ?;
68
105
106
+ for ( key, value) in & self . custom_metadata {
107
+ line. add_quoted_field ( key, value) ?;
108
+ }
109
+
69
110
if let Some ( len) = self . req . extensions ( ) . find :: < u64 > ( ) {
70
111
line. add_field ( "metadata_length" , len) ?;
71
112
}
0 commit comments