Skip to content

Commit a50b9b9

Browse files
authored
Merge pull request #204 from http-rs/standardize-trace
Standardize trace
2 parents bbd63d8 + 677e280 commit a50b9b9

File tree

2 files changed

+97
-61
lines changed

2 files changed

+97
-61
lines changed

src/headers/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ pub const SERVER_TIMING: HeaderName = HeaderName::from_lowercase_str("server-tim
143143
/// The `Te` Header
144144
pub const TE: HeaderName = HeaderName::from_lowercase_str("te");
145145

146+
/// The `Traceparent` Header
147+
pub const TRACEPARENT: HeaderName = HeaderName::from_lowercase_str("traceparent");
148+
146149
/// The `Trailer` Header
147150
pub const TRAILER: HeaderName = HeaderName::from_lowercase_str("trailer");
148151

src/trace/trace_context.rs

Lines changed: 94 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use rand::Rng;
22
use std::fmt;
33

4-
use crate::Headers;
4+
use crate::headers::{HeaderName, HeaderValue, Headers, TRACEPARENT};
5+
use crate::Status;
56

6-
/// Extract and inject [Trace-Context](https://w3c.github.io/trace-context/) headers.
7+
/// Extract and apply [Trace-Context](https://w3c.github.io/trace-context/) headers.
78
///
8-
/// ## Examples
9+
/// # Examples
910
///
1011
/// ```
12+
/// # fn main() -> http_types::Result<()> {
13+
/// #
1114
/// use http_types::trace::TraceContext;
1215
///
1316
/// let mut res = http_types::Response::new(200);
@@ -17,14 +20,16 @@ use crate::Headers;
1720
/// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
1821
/// );
1922
///
20-
/// let context = TraceContext::extract(&res).unwrap();
23+
/// let context = TraceContext::from_headers(&res)?.unwrap();
2124
///
2225
/// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16);
2326
/// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16);
2427
///
2528
/// assert_eq!(context.trace_id(), trace_id.unwrap());
2629
/// assert_eq!(context.parent_id(), parent_id.ok());
2730
/// assert_eq!(context.sampled(), true);
31+
/// #
32+
/// # Ok(()) }
2833
/// ```
2934
#[derive(Debug)]
3035
pub struct TraceContext {
@@ -36,10 +41,44 @@ pub struct TraceContext {
3641
}
3742

3843
impl TraceContext {
44+
/// Generate a new TraceContect object without a parent.
45+
///
46+
/// By default root TraceContext objects are sampled.
47+
/// To mark it unsampled, call `context.set_sampled(false)`.
48+
///
49+
/// # Examples
50+
/// ```
51+
/// use http_types::trace::TraceContext;
52+
///
53+
/// let context = TraceContext::new();
54+
///
55+
/// assert_eq!(context.parent_id(), None);
56+
/// assert_eq!(context.sampled(), true);
57+
/// ```
58+
pub fn new() -> Self {
59+
let mut rng = rand::thread_rng();
60+
61+
Self {
62+
id: rng.gen(),
63+
version: 0,
64+
trace_id: rng.gen(),
65+
parent_id: None,
66+
flags: 1,
67+
}
68+
}
69+
3970
/// Create and return TraceContext object based on `traceparent` HTTP header.
4071
///
41-
/// ## Examples
72+
/// # Errors
73+
///
74+
/// This function may error if the header is malformed. An error with a
75+
/// status code of `400: Bad Request` will be generated.
76+
///
77+
/// # Examples
78+
///
4279
/// ```
80+
/// # fn main() -> http_types::Result<()> {
81+
/// #
4382
/// use http_types::trace::TraceContext;
4483
///
4584
/// let mut res = http_types::Response::new(200);
@@ -48,65 +87,43 @@ impl TraceContext {
4887
/// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
4988
/// );
5089
///
51-
/// let context = TraceContext::extract(&res).unwrap();
90+
/// let context = TraceContext::from_headers(&res)?.unwrap();
5291
///
5392
/// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16);
5493
/// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16);
5594
///
5695
/// assert_eq!(context.trace_id(), trace_id.unwrap());
5796
/// assert_eq!(context.parent_id(), parent_id.ok());
5897
/// assert_eq!(context.sampled(), true);
98+
/// #
99+
/// # Ok(()) }
59100
/// ```
60-
pub fn extract(headers: impl AsRef<Headers>) -> crate::Result<Self> {
101+
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
61102
let headers = headers.as_ref();
62103
let mut rng = rand::thread_rng();
63104

64-
let traceparent = match headers.get("traceparent") {
65-
Some(header) => header.as_str(),
66-
None => return Ok(Self::new_root()),
105+
let traceparent = match headers.get(TRACEPARENT) {
106+
Some(header) => header,
107+
None => return Ok(None),
67108
};
109+
let parts: Vec<&str> = traceparent.as_str().split('-').collect();
68110

69-
let parts: Vec<&str> = traceparent.split('-').collect();
70-
71-
Ok(Self {
111+
Ok(Some(Self {
72112
id: rng.gen(),
73113
version: u8::from_str_radix(parts[0], 16)?,
74-
trace_id: u128::from_str_radix(parts[1], 16)?,
75-
parent_id: Some(u64::from_str_radix(parts[2], 16)?),
76-
flags: u8::from_str_radix(parts[3], 16)?,
77-
})
78-
}
79-
80-
/// Generate a new TraceContect object without a parent.
81-
///
82-
/// By default root TraceContext objects are sampled.
83-
/// To mark it unsampled, call `context.set_sampled(false)`.
84-
///
85-
/// ## Examples
86-
/// ```
87-
/// use http_types::trace::TraceContext;
88-
///
89-
/// let context = TraceContext::new_root();
90-
///
91-
/// assert_eq!(context.parent_id(), None);
92-
/// assert_eq!(context.sampled(), true);
93-
/// ```
94-
pub fn new_root() -> Self {
95-
let mut rng = rand::thread_rng();
96-
97-
Self {
98-
id: rng.gen(),
99-
version: 0,
100-
trace_id: rng.gen(),
101-
parent_id: None,
102-
flags: 1,
103-
}
114+
trace_id: u128::from_str_radix(parts[1], 16).status(400)?,
115+
parent_id: Some(u64::from_str_radix(parts[2], 16).status(400)?),
116+
flags: u8::from_str_radix(parts[3], 16).status(400)?,
117+
}))
104118
}
105119

106120
/// Add the traceparent header to the http headers
107121
///
108-
/// ## Examples
122+
/// # Examples
123+
///
109124
/// ```
125+
/// # fn main() -> http_types::Result<()> {
126+
/// #
110127
/// use http_types::trace::TraceContext;
111128
/// use http_types::{Request, Response, Url, Method};
112129
///
@@ -116,20 +133,33 @@ impl TraceContext {
116133
/// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
117134
/// );
118135
///
119-
/// let parent = TraceContext::extract(&req).unwrap();
136+
/// let parent = TraceContext::from_headers(&req)?.unwrap();
120137
///
121138
/// let mut res = Response::new(200);
122-
/// parent.inject(&mut res);
139+
/// parent.apply(&mut res);
123140
///
124-
/// let child = TraceContext::extract(&res).unwrap();
141+
/// let child = TraceContext::from_headers(&res)?.unwrap();
125142
///
126143
/// assert_eq!(child.version(), parent.version());
127144
/// assert_eq!(child.trace_id(), parent.trace_id());
128145
/// assert_eq!(child.parent_id(), Some(parent.id()));
146+
/// #
147+
/// # Ok(()) }
129148
/// ```
130-
pub fn inject(&self, mut headers: impl AsMut<Headers>) {
149+
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
131150
let headers = headers.as_mut();
132-
headers.insert("traceparent", format!("{}", self));
151+
headers.insert(TRACEPARENT, self.value());
152+
}
153+
154+
/// Get the `HeaderName`.
155+
pub fn name(&self) -> HeaderName {
156+
TRACEPARENT
157+
}
158+
159+
/// Get the `HeaderValue`.
160+
pub fn value(&self) -> HeaderValue {
161+
let output = format!("{}", self);
162+
unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
133163
}
134164

135165
/// Generate a child of the current TraceContext and return it.
@@ -175,29 +205,33 @@ impl TraceContext {
175205

176206
/// Returns true if the trace is sampled
177207
///
178-
/// ## Examples
208+
/// # Examples
179209
///
180210
/// ```
211+
/// # fn main() -> http_types::Result<()> {
212+
/// #
181213
/// use http_types::trace::TraceContext;
182214
/// use http_types::Response;
183215
///
184216
/// let mut res = Response::new(200);
185217
/// res.insert_header("traceparent", "00-00000000000000000000000000000001-0000000000000002-01");
186-
/// let context = TraceContext::extract(&res).unwrap();
218+
/// let context = TraceContext::from_headers(&res)?.unwrap();
187219
/// assert_eq!(context.sampled(), true);
220+
/// #
221+
/// # Ok(()) }
188222
/// ```
189223
pub fn sampled(&self) -> bool {
190224
(self.flags & 0b00000001) == 1
191225
}
192226

193227
/// Change sampled flag
194228
///
195-
/// ## Examples
229+
/// # Examples
196230
///
197231
/// ```
198232
/// use http_types::trace::TraceContext;
199233
///
200-
/// let mut context = TraceContext::new_root();
234+
/// let mut context = TraceContext::new();
201235
/// assert_eq!(context.sampled(), true);
202236
/// context.set_sampled(false);
203237
/// assert_eq!(context.sampled(), false);
@@ -225,8 +259,8 @@ mod test {
225259
#[test]
226260
fn default() -> crate::Result<()> {
227261
let mut headers = crate::Headers::new();
228-
headers.insert("traceparent", "00-01-deadbeef-00");
229-
let context = TraceContext::extract(&mut headers)?;
262+
headers.insert(TRACEPARENT, "00-01-deadbeef-00");
263+
let context = TraceContext::from_headers(&mut headers)?.unwrap();
230264
assert_eq!(context.version(), 0);
231265
assert_eq!(context.trace_id(), 1);
232266
assert_eq!(context.parent_id().unwrap(), 3735928559);
@@ -237,8 +271,7 @@ mod test {
237271

238272
#[test]
239273
fn no_header() -> crate::Result<()> {
240-
let mut headers = crate::Headers::new();
241-
let context = TraceContext::extract(&mut headers)?;
274+
let context = TraceContext::new();
242275
assert_eq!(context.version(), 0);
243276
assert_eq!(context.parent_id(), None);
244277
assert_eq!(context.flags, 1);
@@ -249,17 +282,17 @@ mod test {
249282
#[test]
250283
fn not_sampled() -> crate::Result<()> {
251284
let mut headers = crate::Headers::new();
252-
headers.insert("traceparent", "00-01-02-00");
253-
let context = TraceContext::extract(&mut headers)?;
285+
headers.insert(TRACEPARENT, "00-01-02-00");
286+
let context = TraceContext::from_headers(&mut headers)?.unwrap();
254287
assert_eq!(context.sampled(), false);
255288
Ok(())
256289
}
257290

258291
#[test]
259292
fn sampled() -> crate::Result<()> {
260293
let mut headers = crate::Headers::new();
261-
headers.insert("traceparent", "00-01-02-01");
262-
let context = TraceContext::extract(&mut headers)?;
294+
headers.insert(TRACEPARENT, "00-01-02-01");
295+
let context = TraceContext::from_headers(&mut headers)?.unwrap();
263296
assert_eq!(context.sampled(), true);
264297
Ok(())
265298
}

0 commit comments

Comments
 (0)