opentelemetry_spanprocessor_any/sdk/trace/id_generator/
aws.rs

1use crate::sdk;
2use crate::trace::{IdGenerator, SpanId, TraceId};
3use std::time::{Duration, UNIX_EPOCH};
4
5/// Generates AWS X-Ray compliant Trace and Span ids.
6///
7/// Generates OpenTelemetry formatted `TraceId`'s and `SpanId`'s. The `TraceId`'s are generated so
8/// they can be backed out into X-Ray format by the [AWS X-Ray Exporter][xray-exporter] in the
9/// [OpenTelemetry Collector][otel-collector].
10///
11/// ## Trace ID Format
12///
13/// A `trace_id` consists of three numbers separated by hyphens. For example, `1-58406520-a006649127e371903a2de979`.
14/// This includes:
15///
16/// * The version number, that is, 1.
17/// * The time of the original request, in Unix epoch time, in 8 hexadecimal digits.
18/// * For example, 10:00AM December 1st, 2016 PST in epoch time is 1480615200 seconds, or 58406520 in hexadecimal digits.
19/// * A 96-bit identifier for the trace, globally unique, in 24 hexadecimal digits.
20///
21/// See the [AWS X-Ray Documentation][xray-trace-id] for more details.
22///
23/// ## Example
24///
25/// ```
26/// use opentelemetry::trace::noop::NoopSpanExporter;
27/// use opentelemetry::sdk::trace::{self, TracerProvider, XrayIdGenerator};
28///
29/// let _provider: TracerProvider = TracerProvider::builder()
30///     .with_simple_exporter(NoopSpanExporter::new())
31///     .with_config(trace::config().with_id_generator(XrayIdGenerator::default()))
32///     .build();
33/// ```
34///
35/// [otel-collector]: https://github.com/open-telemetry/opentelemetry-collector-contrib#opentelemetry-collector-contrib
36/// [xray-exporter]: https://godoc.org/github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter
37/// [xray-trace-id]: https://docs.aws.amazon.com/xray/latest/devguide/xray-api-sendingdata.html#xray-api-traceids
38#[derive(Debug, Default)]
39pub struct XrayIdGenerator {
40    sdk_default_generator: sdk::trace::IdGenerator,
41}
42
43impl IdGenerator for XrayIdGenerator {
44    /// Generates a new `TraceId` that can be converted to an X-Ray Trace ID
45    fn new_trace_id(&self) -> TraceId {
46        let mut default_trace_id: String =
47            format!("{:024x}", self.sdk_default_generator.new_trace_id());
48
49        default_trace_id.truncate(24);
50
51        let epoch_time_seconds: u64 = crate::time::now()
52            .duration_since(UNIX_EPOCH)
53            .unwrap_or_else(|_| Duration::from_secs(0))
54            .as_secs();
55
56        TraceId::from_hex(format!("{:08x}{}", epoch_time_seconds, default_trace_id).as_str())
57            .unwrap_or(TraceId::INVALID)
58    }
59
60    /// Generates a new `SpanId` that can be converted to an X-Ray Segment ID
61    fn new_span_id(&self) -> SpanId {
62        self.sdk_default_generator.new_span_id()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use std::thread::sleep;
70
71    #[test]
72    fn test_trace_id_generation() {
73        let before: u64 = crate::time::now()
74            .duration_since(UNIX_EPOCH)
75            .unwrap()
76            .as_secs();
77        sleep(Duration::from_secs(1));
78
79        let generator: XrayIdGenerator = XrayIdGenerator::default();
80        let trace_id: TraceId = generator.new_trace_id();
81
82        sleep(Duration::from_secs(1));
83        let after: u64 = crate::time::now()
84            .duration_since(UNIX_EPOCH)
85            .unwrap()
86            .as_secs();
87
88        let trace_as_hex: String = format!("{:032x}", trace_id);
89        let (timestamp, _xray_id) = trace_as_hex.split_at(8_usize);
90
91        let trace_time: u64 = u64::from_str_radix(timestamp, 16).unwrap();
92
93        assert!(before <= trace_time);
94        assert!(after >= trace_time);
95    }
96}