]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
1 | // Copyright The OpenTelemetry Authors |
2 | // SPDX-License-Identifier: Apache-2.0 | |
3 | ||
4 | #pragma once | |
5 | ||
6 | #include "detail/hex.h" | |
7 | #include "detail/string.h" | |
8 | #include "opentelemetry/context/propagation/text_map_propagator.h" | |
9 | #include "opentelemetry/trace/context.h" | |
10 | #include "opentelemetry/trace/default_span.h" | |
11 | ||
12 | #include <array> | |
13 | ||
14 | OPENTELEMETRY_BEGIN_NAMESPACE | |
15 | namespace trace | |
16 | { | |
17 | namespace propagation | |
18 | { | |
19 | ||
20 | static const nostd::string_view kB3CombinedHeader = "b3"; | |
21 | ||
22 | static const nostd::string_view kB3TraceIdHeader = "X-B3-TraceId"; | |
23 | static const nostd::string_view kB3SpanIdHeader = "X-B3-SpanId"; | |
24 | static const nostd::string_view kB3SampledHeader = "X-B3-Sampled"; | |
25 | ||
26 | /* | |
27 | B3, single header: | |
28 | b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90 | |
29 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^ | |
30 | 0 TraceId 31 33 SpanId 48 | 52 ParentSpanId 68 | |
31 | 50 Debug flag | |
32 | Multiheader version: X-B3-Sampled | |
33 | X-B3-TraceId X-B3-SpanId X-B3-ParentSpanId (ignored) | |
34 | */ | |
35 | ||
36 | static const int kTraceIdHexStrLength = 32; | |
37 | static const int kSpanIdHexStrLength = 16; | |
38 | ||
39 | // The B3PropagatorExtractor class provides an interface that enables extracting context from | |
40 | // headers of HTTP requests. HTTP frameworks and clients can integrate with B3Propagator by | |
41 | // providing the object containing the headers, and a getter function for the extraction. Based on: | |
42 | // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md#b3-extract | |
43 | ||
44 | class B3PropagatorExtractor : public opentelemetry::context::propagation::TextMapPropagator | |
45 | { | |
46 | public: | |
47 | // Returns the context that is stored in the HTTP header carrier. | |
48 | context::Context Extract(const opentelemetry::context::propagation::TextMapCarrier &carrier, | |
49 | context::Context &context) noexcept override | |
50 | { | |
51 | SpanContext span_context = ExtractImpl(carrier); | |
52 | nostd::shared_ptr<Span> sp{new DefaultSpan(span_context)}; | |
53 | return trace::SetSpan(context, sp); | |
54 | } | |
55 | ||
56 | static TraceId TraceIdFromHex(nostd::string_view trace_id) | |
57 | { | |
58 | uint8_t buf[kTraceIdHexStrLength / 2]; | |
59 | detail::HexToBinary(trace_id, buf, sizeof(buf)); | |
60 | return TraceId(buf); | |
61 | } | |
62 | ||
63 | static SpanId SpanIdFromHex(nostd::string_view span_id) | |
64 | { | |
65 | uint8_t buf[kSpanIdHexStrLength / 2]; | |
66 | detail::HexToBinary(span_id, buf, sizeof(buf)); | |
67 | return SpanId(buf); | |
68 | } | |
69 | ||
70 | static TraceFlags TraceFlagsFromHex(nostd::string_view trace_flags) | |
71 | { | |
72 | if (trace_flags.length() != 1 || (trace_flags[0] != '1' && trace_flags[0] != 'd')) | |
73 | { // check for invalid length of flags and treat 'd' as sampled | |
74 | return TraceFlags(0); | |
75 | } | |
76 | return TraceFlags(TraceFlags::kIsSampled); | |
77 | } | |
78 | ||
79 | private: | |
80 | static SpanContext ExtractImpl(const opentelemetry::context::propagation::TextMapCarrier &carrier) | |
81 | { | |
82 | nostd::string_view trace_id_hex; | |
83 | nostd::string_view span_id_hex; | |
84 | nostd::string_view trace_flags_hex; | |
85 | ||
86 | // first let's try a single-header variant | |
87 | auto singleB3Header = carrier.Get(kB3CombinedHeader); | |
88 | if (!singleB3Header.empty()) | |
89 | { | |
90 | std::array<nostd::string_view, 3> fields{}; | |
91 | // https://github.com/openzipkin/b3-propagation/blob/master/RATIONALE.md | |
92 | if (detail::SplitString(singleB3Header, '-', fields.data(), 3) < 2) | |
93 | { | |
94 | return SpanContext::GetInvalid(); | |
95 | } | |
96 | ||
97 | trace_id_hex = fields[0]; | |
98 | span_id_hex = fields[1]; | |
99 | trace_flags_hex = fields[2]; | |
100 | } | |
101 | else | |
102 | { | |
103 | trace_id_hex = carrier.Get(kB3TraceIdHeader); | |
104 | span_id_hex = carrier.Get(kB3SpanIdHeader); | |
105 | trace_flags_hex = carrier.Get(kB3SampledHeader); | |
106 | } | |
107 | ||
108 | if (!detail::IsValidHex(trace_id_hex) || !detail::IsValidHex(span_id_hex)) | |
109 | { | |
110 | return SpanContext::GetInvalid(); | |
111 | } | |
112 | ||
113 | TraceId trace_id = TraceIdFromHex(trace_id_hex); | |
114 | SpanId span_id = SpanIdFromHex(span_id_hex); | |
115 | ||
116 | if (!trace_id.IsValid() || !span_id.IsValid()) | |
117 | { | |
118 | return SpanContext::GetInvalid(); | |
119 | } | |
120 | ||
121 | return SpanContext(trace_id, span_id, TraceFlagsFromHex(trace_flags_hex), true); | |
122 | } | |
123 | }; | |
124 | ||
125 | // The B3Propagator class provides interface that enables extracting and injecting context into | |
126 | // single header of HTTP Request. | |
127 | class B3Propagator : public B3PropagatorExtractor | |
128 | { | |
129 | public: | |
130 | // Sets the context for a HTTP header carrier with self defined rules. | |
131 | void Inject(opentelemetry::context::propagation::TextMapCarrier &carrier, | |
132 | const context::Context &context) noexcept override | |
133 | { | |
134 | SpanContext span_context = trace::GetSpan(context)->GetContext(); | |
135 | if (!span_context.IsValid()) | |
136 | { | |
137 | return; | |
138 | } | |
139 | ||
140 | char trace_identity[kTraceIdHexStrLength + kSpanIdHexStrLength + 3]; | |
141 | static_assert(sizeof(trace_identity) == 51, "b3 trace identity buffer size mismatch"); | |
142 | span_context.trace_id().ToLowerBase16(nostd::span<char, 2 * TraceId::kSize>{ | |
143 | &trace_identity[0], static_cast<std::size_t>(kTraceIdHexStrLength)}); | |
144 | trace_identity[kTraceIdHexStrLength] = '-'; | |
145 | span_context.span_id().ToLowerBase16(nostd::span<char, 2 * SpanId::kSize>{ | |
146 | &trace_identity[kTraceIdHexStrLength + 1], static_cast<std::size_t>(kSpanIdHexStrLength)}); | |
147 | trace_identity[kTraceIdHexStrLength + kSpanIdHexStrLength + 1] = '-'; | |
148 | trace_identity[kTraceIdHexStrLength + kSpanIdHexStrLength + 2] = | |
149 | span_context.trace_flags().IsSampled() ? '1' : '0'; | |
150 | ||
151 | carrier.Set(kB3CombinedHeader, nostd::string_view(trace_identity, sizeof(trace_identity))); | |
152 | } | |
153 | ||
154 | bool Fields(nostd::function_ref<bool(nostd::string_view)> callback) const noexcept override | |
155 | { | |
156 | return callback(kB3CombinedHeader); | |
157 | } | |
158 | }; | |
159 | ||
160 | class B3PropagatorMultiHeader : public B3PropagatorExtractor | |
161 | { | |
162 | public: | |
163 | void Inject(opentelemetry::context::propagation::TextMapCarrier &carrier, | |
164 | const context::Context &context) noexcept override | |
165 | { | |
166 | SpanContext span_context = GetSpan(context)->GetContext(); | |
167 | if (!span_context.IsValid()) | |
168 | { | |
169 | return; | |
170 | } | |
171 | char trace_id[32]; | |
172 | TraceId(span_context.trace_id()).ToLowerBase16(trace_id); | |
173 | char span_id[16]; | |
174 | SpanId(span_context.span_id()).ToLowerBase16(span_id); | |
175 | char trace_flags[2]; | |
176 | TraceFlags(span_context.trace_flags()).ToLowerBase16(trace_flags); | |
177 | carrier.Set(kB3TraceIdHeader, nostd::string_view(trace_id, sizeof(trace_id))); | |
178 | carrier.Set(kB3SpanIdHeader, nostd::string_view(span_id, sizeof(span_id))); | |
179 | carrier.Set(kB3SampledHeader, nostd::string_view(trace_flags + 1, 1)); | |
180 | } | |
181 | ||
182 | bool Fields(nostd::function_ref<bool(nostd::string_view)> callback) const noexcept override | |
183 | { | |
184 | return callback(kB3TraceIdHeader) && callback(kB3SpanIdHeader) && callback(kB3SampledHeader); | |
185 | } | |
186 | }; | |
187 | ||
188 | } // namespace propagation | |
189 | } // namespace trace | |
190 | OPENTELEMETRY_END_NAMESPACE |