whiskers.git / crates / whiskersd / src / access.rs

One line per request, written when the response goes out: method, path, status, how long it took and how many bytes came in and went out. Bodies are never logged (they are a child's words), only their sizes.

5use std::io::Read;
6use std::time::Instant;
8use log::{error, info, warn};
9
10pub struct Access {
11    method: String,
12    path: String,
13    started: Instant,
14    bytes_in: usize,
15}
16
17impl Access {
18    pub fn new(request: &tiny_http::Request) -> Self {
19        Self { method: request.method().to_string(), path: request.url().to_owned(), started: Instant::now(), bytes_in: 0 }
20    }

Notes how large the request body turned out to be.

23    pub fn read(&mut self, bytes_in: usize) {
24        self.bytes_in = bytes_in;
25    }

Sends the response and logs the request: errors at error, refusals at warn, the rest at info.

28    pub fn respond<R: Read>(&self, request: tiny_http::Request, response: tiny_http::Response<R>) {
29        let status = response.status_code().0;
30        let bytes_out = response.data_length();
31        let ms = self.started.elapsed().as_millis();
32        let out = bytes_out.map_or_else(|| "?".to_owned(), |n| n.to_string());
33        let (m, p, b) = (&self.method, &self.path, self.bytes_in);
34        if status >= 500 {
35            error!(target: "access", "{m} {p} -> {status} in {ms} ms, {b} bytes in, {out} out");
36        } else if status >= 400 {
37            warn!(target: "access", "{m} {p} -> {status} in {ms} ms, {b} bytes in, {out} out");
38        } else {
39            info!(target: "access", "{m} {p} -> {status} in {ms} ms, {b} bytes in, {out} out");
40        }
41        if let Err(e) = request.respond(response) {
42            warn!(target: "access", "{m} {p}: the client went away before the response was sent: {e}");
43        }
44    }
45}