1use crate::constants;
39use crate::error;
40use crate::{Error, ErrorKind, Result};
41use cf_rustracing::carrier::{
42 ExtractFromBinary, ExtractFromHttpHeader, ExtractFromTextMap, InjectToBinary,
43 InjectToHttpHeader, InjectToTextMap, IterHttpHeaderFields, SetHttpHeaderField, TextMap,
44};
45use cf_rustracing::sampler::BoxSampler;
46use percent_encoding::percent_decode;
47use std::fmt;
48use std::io::{Read, Write};
49use std::str::{self, FromStr};
50
51pub type Span = cf_rustracing::span::Span<SpanContextState>;
53
54pub type SpanHandle = cf_rustracing::span::SpanHandle<SpanContextState>;
56
57pub type FinishedSpan = cf_rustracing::span::FinishedSpan<SpanContextState>;
59
60pub type SpanReceiver = cf_rustracing::span::SpanReceiver<SpanContextState>;
62
63pub type SpanSender = cf_rustracing::span::SpanSender<SpanContextState>;
65
66pub type StartSpanOptions<'a> =
68 cf_rustracing::span::StartSpanOptions<'a, BoxSampler<SpanContextState>, SpanContextState>;
69
70pub type CandidateSpan<'a> = cf_rustracing::span::CandidateSpan<'a, SpanContextState>;
72
73pub type SpanContext = cf_rustracing::span::SpanContext<SpanContextState>;
75
76pub type SpanReference = cf_rustracing::span::SpanReference<SpanContextState>;
78
79const FLAG_SAMPLED: u8 = 0b01;
80const FLAG_DEBUG: u8 = 0b10;
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
96#[allow(missing_docs)]
97pub struct TraceId {
98 pub high: u64,
99 pub low: u64,
100}
101impl TraceId {
102 pub fn new() -> Self {
104 TraceId::default()
105 }
106}
107impl Default for TraceId {
108 fn default() -> Self {
110 TraceId {
111 high: rand::random(),
112 low: rand::random(),
113 }
114 }
115}
116impl fmt::Display for TraceId {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 if self.high == 0 {
119 write!(f, "{:x}", self.low)
120 } else {
121 write!(f, "{:x}{:016x}", self.high, self.low)
122 }
123 }
124}
125impl FromStr for TraceId {
126 type Err = Error;
127 fn from_str(s: &str) -> Result<Self> {
128 if s.len() <= 16 {
129 let low = u64::from_str_radix(s, 16).map_err(error::from_parse_int_error)?;
130 Ok(TraceId { high: 0, low })
131 } else if s.len() <= 32 {
132 let (high, low) = s.as_bytes().split_at(s.len() - 16);
133 let high = str::from_utf8(high).map_err(error::from_utf8_error)?;
134 let high = u64::from_str_radix(high, 16).map_err(error::from_parse_int_error)?;
135
136 let low = str::from_utf8(low).map_err(error::from_utf8_error)?;
137 let low = u64::from_str_radix(low, 16).map_err(error::from_parse_int_error)?;
138 Ok(TraceId { high, low })
139 } else {
140 track_panic!(ErrorKind::InvalidInput, "s={:?}", s)
141 }
142 }
143}
144
145#[derive(Debug, Clone)]
155pub struct SpanContextStateBuilder {
156 trace_id: Option<TraceId>,
157 span_id: Option<u64>,
158 flags: u8,
159 debug_id: String,
160}
161impl SpanContextStateBuilder {
162 pub fn new() -> Self {
164 SpanContextStateBuilder {
165 trace_id: None,
166 span_id: None,
167 flags: FLAG_SAMPLED,
168 debug_id: String::new(),
169 }
170 }
171
172 pub fn trace_id(mut self, trace_id: TraceId) -> Self {
176 self.trace_id = Some(trace_id);
177 self
178 }
179
180 pub fn span_id(mut self, span_id: u64) -> Self {
184 self.span_id = Some(span_id);
185 self
186 }
187
188 pub fn debug_id(mut self, debug_id: String) -> Self {
192 if !debug_id.is_empty() {
193 self.flags |= FLAG_DEBUG;
194 self.debug_id = debug_id;
195 }
196 self
197 }
198
199 pub fn finish(self) -> SpanContextState {
201 SpanContextState {
202 trace_id: self.trace_id.unwrap_or_default(),
203 span_id: self.span_id.unwrap_or_else(rand::random),
204 flags: self.flags,
205 debug_id: self.debug_id,
206 }
207 }
208}
209impl Default for SpanContextStateBuilder {
210 fn default() -> Self {
211 Self::new()
212 }
213}
214
215#[derive(Debug, Clone)]
217pub struct SpanContextState {
218 trace_id: TraceId,
219 span_id: u64,
220 flags: u8,
221 debug_id: String,
222}
223impl SpanContextState {
224 pub fn new(trace_id: TraceId, span_id: u64, flags: u8, debug_id: String) -> Self {
226 Self {
227 trace_id,
228 span_id,
229 flags,
230 debug_id,
231 }
232 }
233
234 pub fn trace_id(&self) -> TraceId {
236 self.trace_id
237 }
238
239 pub fn span_id(&self) -> u64 {
241 self.span_id
242 }
243
244 pub fn is_sampled(&self) -> bool {
246 (self.flags & FLAG_SAMPLED) != 0
247 }
248
249 pub fn debug_id(&self) -> Option<&str> {
251 if self.debug_id.is_empty() {
252 None
253 } else {
254 Some(&self.debug_id)
255 }
256 }
257
258 fn set_debug_id(&mut self, debug_id: String) {
259 if !debug_id.is_empty() {
260 self.flags |= FLAG_DEBUG;
261 self.debug_id = debug_id;
262 }
263 }
264
265 pub fn flags(&self) -> u8 {
267 self.flags
268 }
269
270 fn root() -> Self {
271 Self::with_trace_id(TraceId::default())
272 }
273
274 fn with_trace_id(trace_id: TraceId) -> Self {
275 SpanContextState {
276 trace_id,
277 span_id: rand::random(),
278 flags: FLAG_SAMPLED,
279 debug_id: String::new(),
280 }
281 }
282}
283impl fmt::Display for SpanContextState {
284 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285 let dummy_parent_id = 0;
286 write!(
287 f,
288 "{}:{:x}:{:x}:{:x}",
289 self.trace_id, self.span_id, dummy_parent_id, self.flags
290 )
291 }
292}
293impl FromStr for SpanContextState {
294 type Err = Error;
295 fn from_str(s: &str) -> Result<Self> {
296 let mut tokens = s.splitn(4, ':');
297
298 macro_rules! token {
299 () => {
300 track_assert_some!(tokens.next(), ErrorKind::InvalidInput)
301 };
302 }
303 let trace_id = token!().parse()?;
304 let span_id = u64::from_str_radix(token!(), 16).map_err(error::from_parse_int_error)?;
305 let _parent_span_id =
306 u64::from_str_radix(token!(), 16).map_err(error::from_parse_int_error)?;
307 let flags = u8::from_str_radix(token!(), 16).map_err(error::from_parse_int_error)?;
308
309 Ok(SpanContextState {
310 trace_id,
311 span_id,
312 flags,
313 debug_id: String::new(),
314 })
315 }
316}
317impl<'a> From<CandidateSpan<'a>> for SpanContextState {
318 fn from(f: CandidateSpan<'a>) -> Self {
319 if let Some(primary) = f.references().first() {
320 Self::with_trace_id(primary.span().trace_id)
321 } else {
322 Self::root()
323 }
324 }
325}
326impl<T: TextMap> InjectToTextMap<T> for SpanContextState {
327 fn inject_to_text_map(context: &SpanContext, carrier: &mut T) -> Result<()> {
328 carrier.set(
330 constants::TRACER_CONTEXT_HEADER_NAME,
331 &context.state().to_string(),
332 );
333 Ok(())
334 }
335}
336impl<T: TextMap> ExtractFromTextMap<T> for SpanContextState {
337 fn extract_from_text_map(carrier: &T) -> Result<Option<SpanContext>> {
338 use std::collections::HashMap;
339
340 let mut map = HashMap::new();
342 if let Some(v) = carrier.get(constants::TRACER_CONTEXT_HEADER_NAME) {
343 map.insert(constants::TRACER_CONTEXT_HEADER_NAME, v);
344 }
345 if let Some(v) = carrier.get(constants::JAEGER_DEBUG_HEADER) {
346 map.insert(constants::JAEGER_DEBUG_HEADER, v);
347 }
348 Self::extract_from_http_header(&map)
349 }
350}
351impl<T> InjectToHttpHeader<T> for SpanContextState
352where
353 T: SetHttpHeaderField,
354{
355 fn inject_to_http_header(context: &SpanContext, carrier: &mut T) -> Result<()> {
356 carrier.set_http_header_field(
358 constants::TRACER_CONTEXT_HEADER_NAME,
359 &context.state().to_string(),
360 )?;
361 Ok(())
362 }
363}
364impl<'a, T> ExtractFromHttpHeader<'a, T> for SpanContextState
365where
366 T: IterHttpHeaderFields<'a>,
367{
368 fn extract_from_http_header(carrier: &'a T) -> Result<Option<SpanContext>> {
369 let mut state: Option<SpanContextState> = None;
370 let mut debug_id = None;
371 let baggage_items = Vec::new(); for (name, value) in carrier.fields() {
373 if name.eq_ignore_ascii_case(constants::TRACER_CONTEXT_HEADER_NAME) {
374 let value = percent_decode(value);
375 let value = value.decode_utf8().map_err(error::from_utf8_error)?;
376 state = Some(value.parse()?);
377 } else if name.eq_ignore_ascii_case(constants::JAEGER_DEBUG_HEADER) {
378 let value = str::from_utf8(value).map_err(error::from_utf8_error)?;
379 debug_id = Some(value.to_owned());
380 }
381 }
382 if let Some(mut state) = state {
383 if let Some(debug_id) = debug_id.take() {
384 state.set_debug_id(debug_id);
385 }
386 Ok(Some(SpanContext::new(state, baggage_items)))
387 } else if let Some(debug_id) = debug_id.take() {
388 let state = SpanContextState {
389 trace_id: TraceId { high: 0, low: 0 },
390 span_id: 0,
391 flags: FLAG_DEBUG,
392 debug_id,
393 };
394 Ok(Some(SpanContext::new(state, Vec::new())))
395 } else {
396 Ok(None)
397 }
398 }
399}
400impl<T> InjectToBinary<T> for SpanContextState
401where
402 T: Write,
403{
404 fn inject_to_binary(context: &SpanContext, carrier: &mut T) -> Result<()> {
405 let mut u64buf: [u8; 8] = context.state().trace_id.high.to_be_bytes();
406 let u32buf: [u8; 4] = [0; 4]; let u8buf: [u8; 1] = [context.state().flags];
408
409 carrier.write(&u64buf).map_err(error::from_io_error)?;
410 u64buf = context.state().trace_id.low.to_be_bytes();
411 carrier.write(&u64buf).map_err(error::from_io_error)?;
412 u64buf = context.state().span_id.to_be_bytes();
413 carrier.write(&u64buf).map_err(error::from_io_error)?;
414 u64buf = [0; 8];
416 carrier.write(&u64buf).map_err(error::from_io_error)?;
417 carrier.write(&u8buf).map_err(error::from_io_error)?;
418 carrier.write(&u32buf).map_err(error::from_io_error)?;
419
420 Ok(())
421 }
422}
423impl<T> ExtractFromBinary<T> for SpanContextState
424where
425 T: Read,
426{
427 fn extract_from_binary(carrier: &mut T) -> Result<Option<SpanContext>> {
428 let baggage_items = Vec::new(); let mut u64buf: [u8; 8] = [0; 8];
431 let mut u8buf: [u8; 1] = [0; 1];
432
433 carrier
434 .read(&mut u64buf[..])
435 .map_err(error::from_io_error)?;
436 let trace_id_high = u64::from_be_bytes(u64buf);
437 carrier
438 .read(&mut u64buf[..])
439 .map_err(error::from_io_error)?;
440 let trace_id_low = u64::from_be_bytes(u64buf);
441 carrier
442 .read(&mut u64buf[..])
443 .map_err(error::from_io_error)?;
444 let span_id = u64::from_be_bytes(u64buf);
445 carrier
446 .read(&mut u64buf[..])
447 .map_err(error::from_io_error)?;
448 carrier.read(&mut u8buf[..]).map_err(error::from_io_error)?;
450 let flags = u8buf[0];
451
452 let state = SpanContextState {
453 trace_id: TraceId {
454 high: trace_id_high,
455 low: trace_id_low,
456 },
457 span_id,
458 flags,
459 debug_id: String::new(),
460 };
461 Ok(Some(SpanContext::new(state, baggage_items)))
462 }
463}
464
465#[cfg(test)]
466mod test {
467 use super::*;
468 use crate::Tracer;
469 use cf_rustracing::sampler::AllSampler;
470 use std::collections::HashMap;
471 use std::io::Cursor;
472 use trackable::error::Failed;
473 use trackable::result::TestResult;
474
475 #[test]
476 fn trace_id_conversion_works() {
477 let id = TraceId { high: 0, low: 10 };
478 assert_eq!(id.to_string(), "a");
479 assert_eq!("a".parse::<TraceId>().unwrap(), id);
480
481 let id = TraceId { high: 1, low: 2 };
482 assert_eq!(id.to_string(), "10000000000000002");
483 assert_eq!("10000000000000002".parse::<TraceId>().unwrap(), id);
484 }
485
486 #[test]
487 fn sampled_flag_works() {
488 let state: SpanContextState = "6309ab92c95468edea0dc1a9772ae2dc:409423a204bc17a8:0:1"
489 .parse()
490 .unwrap();
491
492 assert!(state.is_sampled());
493 assert_eq!(state.flags(), 1);
494
495 let state: SpanContextState = "6309ab92c95468edea0dc1a9772ae2dc:409423a204bc17a8:0:0"
496 .parse()
497 .unwrap();
498
499 assert!(!state.is_sampled());
500 assert_eq!(state.flags(), 0);
501 }
502
503 #[tokio::test]
504 async fn inject_to_text_map_works() -> TestResult {
505 let (tracer, _span_rx) = Tracer::new(AllSampler);
506 let span = tracer.span("test").start();
507 let context = track_assert_some!(span.context(), Failed);
508
509 let mut map = HashMap::new();
510 context.inject_to_text_map(&mut map)?;
511 assert!(map.contains_key(constants::TRACER_CONTEXT_HEADER_NAME));
512
513 Ok(())
514 }
515
516 #[test]
517 fn extract_from_text_map_works() -> TestResult {
518 let mut map = HashMap::new();
519 map.insert(
520 constants::TRACER_CONTEXT_HEADER_NAME.to_string(),
521 "6309ab92c95468edea0dc1a9772ae2dc:409423a204bc17a8:0:1".to_string(),
522 );
523 let context = SpanContext::extract_from_text_map(&map)?;
524 let context = track_assert_some!(context, Failed);
525 let trace_id = context.state().trace_id();
526 assert_eq!(trace_id.to_string(), "6309ab92c95468edea0dc1a9772ae2dc");
527
528 Ok(())
529 }
530
531 #[test]
534 fn extract_from_urlencoded_text_map_works() -> TestResult {
535 let mut map = HashMap::new();
536 map.insert(
537 constants::TRACER_CONTEXT_HEADER_NAME.to_string(),
538 "6309ab92c95468edea0dc1a9772ae2dc%3A409423a204bc17a8%3A0%3A1".to_string(),
539 );
540 let context = SpanContext::extract_from_text_map(&map)?;
541 let context = track_assert_some!(context, Failed);
542 let trace_id = context.state().trace_id();
543 assert_eq!(trace_id.to_string(), "6309ab92c95468edea0dc1a9772ae2dc");
544
545 Ok(())
546 }
547
548 #[test]
549 fn extract_debug_id_works() -> TestResult {
550 let mut map = HashMap::new();
551 map.insert(
552 constants::JAEGER_DEBUG_HEADER.to_string(),
553 "abcdef".to_string(),
554 );
555 let context = SpanContext::extract_from_text_map(&map)?;
556 let context = track_assert_some!(context, Failed);
557 let debug_id = context.state().debug_id();
558 assert_eq!(debug_id, Some("abcdef"));
559
560 Ok(())
561 }
562
563 #[test]
564 fn inject_to_binary_works() -> TestResult {
565 let (tracer, _span_rx) = Tracer::new(AllSampler);
566 let parent_span = tracer.span("parent_span_test").start();
567 let span = tracer
568 .span("span_to_be_injected_test")
569 .child_of(parent_span.context().unwrap())
570 .start();
571 let context = track_assert_some!(span.context(), Failed);
572
573 let mut span_buf: Cursor<Vec<u8>> = Cursor::new(vec![]);
574 context.clone().inject_to_binary(&mut span_buf)?;
575
576 let sbv = span_buf.get_ref().to_vec();
578 let mut u64buf: [u8; 8] = [0; 8];
579 let mut u32buf: [u8; 4] = [0; 4];
580 let mut u8buf: [u8; 1] = [0; 1];
581
582 u64buf.copy_from_slice(&sbv[0..8]);
583 assert_eq!(context.state().trace_id().high, u64::from_be_bytes(u64buf));
584 u64buf.copy_from_slice(&sbv[8..16]);
585 assert_eq!(context.state().trace_id().low, u64::from_be_bytes(u64buf));
586 u64buf.copy_from_slice(&sbv[16..24]);
587 assert_eq!(context.state().span_id(), u64::from_be_bytes(u64buf));
588 u64buf.copy_from_slice(&sbv[24..32]);
589 assert_eq!(0, u64::from_be_bytes(u64buf)); u8buf.copy_from_slice(&sbv[32..33]);
591 assert_eq!(context.state().flags(), u8buf[0]);
592 u32buf.copy_from_slice(&sbv[33..37]);
593 assert_eq!(0, u32::from_be_bytes(u32buf)); Ok(())
596 }
597
598 #[test]
599 fn extract_from_binary_works() -> TestResult {
600 let mut span_buf: Cursor<Vec<u8>> = Cursor::new(vec![
601 0xab, 0xcd, 0xef, 0xed, 0xcb, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0xbc, 0xde, 0xfe,
602 0xdc, 0xba, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, 1, 0, 0, 0, 0, ]);
608
609 let context = SpanContext::extract_from_binary(&mut span_buf)?;
610 let context = track_assert_some!(context, Failed);
611 assert_eq!(
612 context.state().trace_id().to_string(),
613 "abcdefedcbabcdeffedcbabcdefedcba"
614 );
615 assert_eq!(context.state().span_id(), 11);
616 assert_eq!(context.state().flags(), 1);
617
618 let (tracer, _span_rx) = Tracer::new(AllSampler);
620 tracer
621 .span("test_from_spancontext")
622 .child_of(&context)
623 .start();
624
625 Ok(())
626 }
627}