surf/middleware/logger/
native.rs1use crate::middleware::{Middleware, Next};
2use crate::{Client, Request, Response};
3
4use std::fmt::Arguments;
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::time;
7
8static COUNTER: AtomicUsize = AtomicUsize::new(0);
9
10#[derive(Debug, Default)]
12pub struct Logger {
13 _priv: (),
14}
15
16impl Logger {
17 pub fn new() -> Self {
19 Logger { _priv: () }
20 }
21}
22
23#[async_trait::async_trait]
24impl Middleware for Logger {
25 #[allow(missing_doc_code_examples)]
26 async fn handle(
27 &self,
28 req: Request,
29 client: Client,
30 next: Next<'_>,
31 ) -> Result<Response, http_types::Error> {
32 let start_time = time::Instant::now();
33 let uri = format!("{}", req.url());
34 let method = format!("{}", req.method());
35 let id = COUNTER.fetch_add(1, Ordering::Relaxed);
36 print(
37 log::Level::Info,
38 format_args!("sending request"),
39 RequestPairs {
40 id,
41 uri: &uri,
42 method: &method,
43 },
44 );
45
46 let res = next.run(req, client).await?;
47
48 let status = res.status();
49 let elapsed = start_time.elapsed();
50 let level = if status.is_server_error() {
51 log::Level::Error
52 } else if status.is_client_error() {
53 log::Level::Warn
54 } else {
55 log::Level::Info
56 };
57
58 print(
59 level,
60 format_args!("request completed"),
61 ResponsePairs {
62 id,
63 elapsed: &format!("{:?}", elapsed),
64 status: status.into(),
65 },
66 );
67
68 Ok(res)
69 }
70}
71
72struct RequestPairs<'a> {
73 id: usize,
74 method: &'a str,
75 uri: &'a str,
76}
77impl<'a> log::kv::Source for RequestPairs<'a> {
78 fn visit<'kvs>(
79 &'kvs self,
80 visitor: &mut dyn log::kv::Visitor<'kvs>,
81 ) -> Result<(), log::kv::Error> {
82 visitor.visit_pair("req.id".into(), self.id.into())?;
83 visitor.visit_pair("req.method".into(), self.method.into())?;
84 visitor.visit_pair("req.uri".into(), self.uri.into())?;
85 Ok(())
86 }
87}
88
89struct ResponsePairs<'a> {
90 id: usize,
91 status: u16,
92 elapsed: &'a str,
93}
94
95impl<'a> log::kv::Source for ResponsePairs<'a> {
96 fn visit<'kvs>(
97 &'kvs self,
98 visitor: &mut dyn log::kv::Visitor<'kvs>,
99 ) -> Result<(), log::kv::Error> {
100 visitor.visit_pair("req.id".into(), self.id.into())?;
101 visitor.visit_pair("req.status".into(), self.status.into())?;
102 visitor.visit_pair("elapsed".into(), self.elapsed.into())?;
103 Ok(())
104 }
105}
106
107fn print(level: log::Level, msg: Arguments<'_>, key_values: impl log::kv::Source) {
108 if level <= log::STATIC_MAX_LEVEL && level <= log::max_level() {
109 log::logger().log(
110 &log::Record::builder()
111 .args(msg)
112 .key_values(&key_values)
113 .level(level)
114 .target(module_path!())
115 .module_path(Some(module_path!()))
116 .file(Some(file!()))
117 .line(Some(line!()))
118 .build(),
119 );
120 }
121}