simple_server_timing_header/lib.rs
1/*!
2Monitor back-end performance using Server-Timing the HTTP header.
3
4```
5use simple_server_timing_header::Timer;
6
7fn handle_request() {
8 let mut timer = Timer::new();
9 // ... do some stuff
10 timer.add("parse_headers");
11 // ... do some more stuff
12 timer.add("get_db_data");
13 // Generate the header value
14 assert_eq!(timer.header_value(), "parse_headers;dur=0, get_db_data;dur=0");
15}
16```
17*/
18use std::time::Instant;
19
20/// Timer used to share performance metrics to the client using the HTTP Server-Timing header
21/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing
22pub struct Timer {
23 last: Instant,
24 timings: Vec<Timing>,
25}
26
27struct Timing {
28 name: String,
29 /// Time in milliseconds
30 duration: u128,
31}
32
33impl Timer {
34 pub fn new() -> Self {
35 Timer {
36 last: Instant::now(),
37 timings: Vec::new(),
38 }
39 }
40
41 /// Adds a named measurement, counting from the last one.
42 /// Only alphanumeric characters are allowed, other characters are replaced with underscores.
43 pub fn add(&mut self, name: &str) {
44 let now = Instant::now();
45 let duration = now.duration_since(self.last).as_millis();
46 self.last = now;
47 self.timings.push(Timing {
48 name: name.into(),
49 duration,
50 });
51 }
52
53 // Header key for `Server-Timing`
54 pub fn header_key() -> &'static str {
55 "Server-Timing"
56 }
57
58 /// Returns the value for a Server-Timings header.
59 /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing
60 pub fn header_value(&self) -> String {
61 let mut out = String::new();
62 use std::fmt::Write;
63 for timing in self.timings.iter() {
64 // Special characters and spaces are not properly parsed, so we replace them with underscores
65 let name = timing.name.replace(|c: char| !c.is_alphanumeric(), "_");
66 _ = write!(out, "{};dur={}, ", name, timing.duration);
67 }
68 // remove the trailing ", "
69 out.pop();
70 out.pop();
71
72 out
73 }
74}
75
76impl Default for Timer {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82#[cfg(test)]
83mod test {
84 use super::*;
85
86 #[test]
87 fn test() {
88 let mut timer = Timer::new();
89 // ... do some stuff
90 timer.add("parse headers");
91 // ... do some more stuff
92 timer.add("get_db_data");
93 // Generate the header value
94 assert_eq!(
95 timer.header_value(),
96 "parse_headers;dur=0, get_db_data;dur=0"
97 );
98 }
99}