|
1 |
| -//! Extract and inject [trace context](https://w3c.github.io/trace-context/) headers. |
| 1 | +//! HTTP timings and traces. |
2 | 2 | //!
|
3 |
| -//! ## Examples |
| 3 | +//! This module implements parsers and serializers for timing-related headers. |
| 4 | +//! These headers enable tracing and timing requests, and help answer the |
| 5 | +//! question of: _"Where is my program spending its time?"_ |
4 | 6 | //!
|
5 |
| -//! ``` |
6 |
| -//! use http_types::trace::TraceContext; |
| 7 | +//! # Specifications |
7 | 8 | //!
|
8 |
| -//! let mut res = http_types::Response::new(200); |
9 |
| -//! |
10 |
| -//! res.insert_header( |
11 |
| -//! "traceparent", |
12 |
| -//! "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" |
13 |
| -//! ); |
14 |
| -//! |
15 |
| -//! let context = TraceContext::extract(&res).unwrap(); |
16 |
| -//! |
17 |
| -//! let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16); |
18 |
| -//! let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16); |
19 |
| -//! |
20 |
| -//! assert_eq!(context.trace_id(), trace_id.unwrap()); |
21 |
| -//! assert_eq!(context.parent_id(), parent_id.ok()); |
22 |
| -//! assert_eq!(context.sampled(), true); |
23 |
| -//! ``` |
24 |
| -
|
25 |
| -use rand::Rng; |
26 |
| -use std::fmt; |
27 |
| - |
28 |
| -use crate::Headers; |
29 |
| - |
30 |
| -/// A TraceContext object |
31 |
| -#[derive(Debug)] |
32 |
| -pub struct TraceContext { |
33 |
| - id: u64, |
34 |
| - version: u8, |
35 |
| - trace_id: u128, |
36 |
| - parent_id: Option<u64>, |
37 |
| - flags: u8, |
38 |
| -} |
39 |
| - |
40 |
| -impl TraceContext { |
41 |
| - /// Create and return TraceContext object based on `traceparent` HTTP header. |
42 |
| - /// |
43 |
| - /// ## Examples |
44 |
| - /// ``` |
45 |
| - /// use http_types::trace::TraceContext; |
46 |
| - /// |
47 |
| - /// let mut res = http_types::Response::new(200); |
48 |
| - /// res.insert_header( |
49 |
| - /// "traceparent", |
50 |
| - /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" |
51 |
| - /// ); |
52 |
| - /// |
53 |
| - /// let context = TraceContext::extract(&res).unwrap(); |
54 |
| - /// |
55 |
| - /// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16); |
56 |
| - /// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16); |
57 |
| - /// |
58 |
| - /// assert_eq!(context.trace_id(), trace_id.unwrap()); |
59 |
| - /// assert_eq!(context.parent_id(), parent_id.ok()); |
60 |
| - /// assert_eq!(context.sampled(), true); |
61 |
| - /// ``` |
62 |
| - pub fn extract(headers: impl AsRef<Headers>) -> crate::Result<Self> { |
63 |
| - let headers = headers.as_ref(); |
64 |
| - let mut rng = rand::thread_rng(); |
65 |
| - |
66 |
| - let traceparent = match headers.get("traceparent") { |
67 |
| - Some(header) => header.as_str(), |
68 |
| - None => return Ok(Self::new_root()), |
69 |
| - }; |
70 |
| - |
71 |
| - let parts: Vec<&str> = traceparent.split('-').collect(); |
72 |
| - |
73 |
| - Ok(Self { |
74 |
| - id: rng.gen(), |
75 |
| - version: u8::from_str_radix(parts[0], 16)?, |
76 |
| - trace_id: u128::from_str_radix(parts[1], 16)?, |
77 |
| - parent_id: Some(u64::from_str_radix(parts[2], 16)?), |
78 |
| - flags: u8::from_str_radix(parts[3], 16)?, |
79 |
| - }) |
80 |
| - } |
81 |
| - |
82 |
| - /// Generate a new TraceContect object without a parent. |
83 |
| - /// |
84 |
| - /// By default root TraceContext objects are sampled. |
85 |
| - /// To mark it unsampled, call `context.set_sampled(false)`. |
86 |
| - /// |
87 |
| - /// ## Examples |
88 |
| - /// ``` |
89 |
| - /// use http_types::trace::TraceContext; |
90 |
| - /// |
91 |
| - /// let context = TraceContext::new_root(); |
92 |
| - /// |
93 |
| - /// assert_eq!(context.parent_id(), None); |
94 |
| - /// assert_eq!(context.sampled(), true); |
95 |
| - /// ``` |
96 |
| - pub fn new_root() -> Self { |
97 |
| - let mut rng = rand::thread_rng(); |
98 |
| - |
99 |
| - Self { |
100 |
| - id: rng.gen(), |
101 |
| - version: 0, |
102 |
| - trace_id: rng.gen(), |
103 |
| - parent_id: None, |
104 |
| - flags: 1, |
105 |
| - } |
106 |
| - } |
107 |
| - |
108 |
| - /// Add the traceparent header to the http headers |
109 |
| - /// |
110 |
| - /// ## Examples |
111 |
| - /// ``` |
112 |
| - /// use http_types::trace::TraceContext; |
113 |
| - /// use http_types::{Request, Response, Url, Method}; |
114 |
| - /// |
115 |
| - /// let mut req = Request::new(Method::Get, Url::parse("https://example.com").unwrap()); |
116 |
| - /// req.insert_header( |
117 |
| - /// "traceparent", |
118 |
| - /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" |
119 |
| - /// ); |
120 |
| - /// |
121 |
| - /// let parent = TraceContext::extract(&req).unwrap(); |
122 |
| - /// |
123 |
| - /// let mut res = Response::new(200); |
124 |
| - /// parent.inject(&mut res); |
125 |
| - /// |
126 |
| - /// let child = TraceContext::extract(&res).unwrap(); |
127 |
| - /// |
128 |
| - /// assert_eq!(child.version(), parent.version()); |
129 |
| - /// assert_eq!(child.trace_id(), parent.trace_id()); |
130 |
| - /// assert_eq!(child.parent_id(), Some(parent.id())); |
131 |
| - /// ``` |
132 |
| - pub fn inject(&self, mut headers: impl AsMut<Headers>) { |
133 |
| - let headers = headers.as_mut(); |
134 |
| - headers.insert("traceparent", format!("{}", self)); |
135 |
| - } |
136 |
| - |
137 |
| - /// Generate a child of the current TraceContext and return it. |
138 |
| - /// |
139 |
| - /// The child will have a new randomly genrated `id` and its `parent_id` will be set to the |
140 |
| - /// `id` of this TraceContext. |
141 |
| - pub fn child(&self) -> Self { |
142 |
| - let mut rng = rand::thread_rng(); |
143 |
| - |
144 |
| - Self { |
145 |
| - id: rng.gen(), |
146 |
| - version: self.version, |
147 |
| - trace_id: self.trace_id, |
148 |
| - parent_id: Some(self.id), |
149 |
| - flags: self.flags, |
150 |
| - } |
151 |
| - } |
152 |
| - |
153 |
| - /// Return the id of the TraceContext. |
154 |
| - pub fn id(&self) -> u64 { |
155 |
| - self.id |
156 |
| - } |
157 |
| - |
158 |
| - /// Return the version of the TraceContext spec used. |
159 |
| - /// |
160 |
| - /// You probably don't need this. |
161 |
| - pub fn version(&self) -> u8 { |
162 |
| - self.version |
163 |
| - } |
164 |
| - |
165 |
| - /// Return the trace id of the TraceContext. |
166 |
| - /// |
167 |
| - /// All children will have the same `trace_id`. |
168 |
| - pub fn trace_id(&self) -> u128 { |
169 |
| - self.trace_id |
170 |
| - } |
171 |
| - |
172 |
| - /// Return the id of the parent TraceContext. |
173 |
| - #[inline] |
174 |
| - pub fn parent_id(&self) -> Option<u64> { |
175 |
| - self.parent_id |
176 |
| - } |
177 |
| - |
178 |
| - /// Returns true if the trace is sampled |
179 |
| - /// |
180 |
| - /// ## Examples |
181 |
| - /// |
182 |
| - /// ``` |
183 |
| - /// use http_types::trace::TraceContext; |
184 |
| - /// use http_types::Response; |
185 |
| - /// |
186 |
| - /// let mut res = Response::new(200); |
187 |
| - /// res.insert_header("traceparent", "00-00000000000000000000000000000001-0000000000000002-01"); |
188 |
| - /// let context = TraceContext::extract(&res).unwrap(); |
189 |
| - /// assert_eq!(context.sampled(), true); |
190 |
| - /// ``` |
191 |
| - pub fn sampled(&self) -> bool { |
192 |
| - (self.flags & 0b00000001) == 1 |
193 |
| - } |
194 |
| - |
195 |
| - /// Change sampled flag |
196 |
| - /// |
197 |
| - /// ## Examples |
198 |
| - /// |
199 |
| - /// ``` |
200 |
| - /// use http_types::trace::TraceContext; |
201 |
| - /// |
202 |
| - /// let mut context = TraceContext::new_root(); |
203 |
| - /// assert_eq!(context.sampled(), true); |
204 |
| - /// context.set_sampled(false); |
205 |
| - /// assert_eq!(context.sampled(), false); |
206 |
| - /// ``` |
207 |
| - pub fn set_sampled(&mut self, sampled: bool) { |
208 |
| - let x = sampled as u8; |
209 |
| - self.flags ^= (x ^ self.flags) & (1 << 0); |
210 |
| - } |
211 |
| -} |
212 |
| - |
213 |
| -impl fmt::Display for TraceContext { |
214 |
| - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
215 |
| - write!( |
216 |
| - f, |
217 |
| - "{:02x}-{:032x}-{:016x}-{:02x}", |
218 |
| - self.version, self.trace_id, self.id, self.flags |
219 |
| - ) |
220 |
| - } |
221 |
| -} |
222 |
| - |
223 |
| -#[cfg(test)] |
224 |
| -mod test { |
225 |
| - use super::*; |
226 |
| - |
227 |
| - #[test] |
228 |
| - fn default() -> crate::Result<()> { |
229 |
| - let mut headers = crate::Headers::new(); |
230 |
| - headers.insert("traceparent", "00-01-deadbeef-00"); |
231 |
| - let context = TraceContext::extract(&mut headers)?; |
232 |
| - assert_eq!(context.version(), 0); |
233 |
| - assert_eq!(context.trace_id(), 1); |
234 |
| - assert_eq!(context.parent_id().unwrap(), 3735928559); |
235 |
| - assert_eq!(context.flags, 0); |
236 |
| - assert_eq!(context.sampled(), false); |
237 |
| - Ok(()) |
238 |
| - } |
239 |
| - |
240 |
| - #[test] |
241 |
| - fn no_header() -> crate::Result<()> { |
242 |
| - let mut headers = crate::Headers::new(); |
243 |
| - let context = TraceContext::extract(&mut headers)?; |
244 |
| - assert_eq!(context.version(), 0); |
245 |
| - assert_eq!(context.parent_id(), None); |
246 |
| - assert_eq!(context.flags, 1); |
247 |
| - assert_eq!(context.sampled(), true); |
248 |
| - Ok(()) |
249 |
| - } |
| 9 | +//! - [W3C Trace-Context headers](https://w3c.github.io/trace-context/) |
| 10 | +//! - [W3C Server-Timing headers](https://w3c.github.io/server-timing/#the-server-timing-header-field) |
250 | 11 |
|
251 |
| - #[test] |
252 |
| - fn not_sampled() -> crate::Result<()> { |
253 |
| - let mut headers = crate::Headers::new(); |
254 |
| - headers.insert("traceparent", "00-01-02-00"); |
255 |
| - let context = TraceContext::extract(&mut headers)?; |
256 |
| - assert_eq!(context.sampled(), false); |
257 |
| - Ok(()) |
258 |
| - } |
| 12 | +pub mod server_timing; |
| 13 | +mod trace_context; |
259 | 14 |
|
260 |
| - #[test] |
261 |
| - fn sampled() -> crate::Result<()> { |
262 |
| - let mut headers = crate::Headers::new(); |
263 |
| - headers.insert("traceparent", "00-01-02-01"); |
264 |
| - let context = TraceContext::extract(&mut headers)?; |
265 |
| - assert_eq!(context.sampled(), true); |
266 |
| - Ok(()) |
267 |
| - } |
268 |
| -} |
| 15 | +#[doc(inline)] |
| 16 | +pub use server_timing::{Metric, ServerTiming}; |
| 17 | +pub use trace_context::TraceContext; |
0 commit comments