]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Copyright (c) 2017 Uber Technologies, Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #ifndef JAEGERTRACING_PROPAGATION_PROPAGATOR_H | |
18 | #define JAEGERTRACING_PROPAGATION_PROPAGATOR_H | |
19 | ||
20 | #include "jaegertracing/SpanContext.h" | |
21 | #include "jaegertracing/metrics/Metrics.h" | |
22 | #include "jaegertracing/net/URI.h" | |
23 | #include "jaegertracing/platform/Endian.h" | |
24 | #include "jaegertracing/propagation/Extractor.h" | |
25 | #include "jaegertracing/propagation/HeadersConfig.h" | |
26 | #include "jaegertracing/propagation/Injector.h" | |
27 | #include <cctype> | |
28 | #include <climits> | |
29 | #include <opentracing/propagation.h> | |
30 | #include <sstream> | |
31 | ||
32 | namespace jaegertracing { | |
33 | ||
34 | class Tracer; | |
35 | ||
36 | namespace propagation { | |
37 | ||
38 | template <typename ReaderType, typename WriterType> | |
39 | class Propagator : public Extractor<ReaderType>, public Injector<WriterType> { | |
40 | public: | |
41 | using Reader = ReaderType; | |
42 | using Writer = WriterType; | |
43 | using StrMap = SpanContext::StrMap; | |
44 | ||
45 | Propagator() | |
46 | : _headerKeys() | |
47 | , _metrics(metrics::Metrics::makeNullMetrics()) | |
48 | { | |
49 | } | |
50 | ||
51 | Propagator(const HeadersConfig& headerKeys, | |
52 | const std::shared_ptr<metrics::Metrics>& metrics) | |
53 | : _headerKeys(headerKeys) | |
54 | , _metrics(metrics) | |
55 | { | |
56 | } | |
57 | ||
58 | virtual ~Propagator() = default; | |
59 | ||
60 | SpanContext extract(const Reader& reader) const override | |
61 | { | |
62 | SpanContext ctx; | |
63 | StrMap baggage; | |
64 | std::string debugID; | |
65 | const auto result = reader.ForeachKey( | |
66 | [this, &ctx, &debugID, &baggage](const std::string& rawKey, | |
67 | const std::string& value) { | |
68 | const auto key = normalizeKey(rawKey); | |
69 | if (key == _headerKeys.traceContextHeaderName()) { | |
70 | const auto safeValue = decodeValue(value); | |
71 | std::istringstream iss(safeValue); | |
72 | if (!(iss >> ctx) || ctx == SpanContext()) { | |
73 | return opentracing::make_expected_from_error<void>( | |
74 | opentracing::span_context_corrupted_error); | |
75 | } | |
76 | } | |
77 | else if (key == _headerKeys.jaegerDebugHeader()) { | |
78 | debugID = value; | |
79 | } | |
80 | else if (key == _headerKeys.jaegerBaggageHeader()) { | |
81 | for (auto&& pair : parseCommaSeparatedMap(value)) { | |
82 | baggage[pair.first] = pair.second; | |
83 | } | |
84 | } | |
85 | else { | |
86 | const auto prefix = _headerKeys.traceBaggageHeaderPrefix(); | |
87 | if (key.size() >= prefix.size() && | |
88 | key.substr(0, prefix.size()) == prefix) { | |
89 | const auto safeKey = removeBaggageKeyPrefix(key); | |
90 | const auto safeValue = decodeValue(value); | |
91 | baggage[safeKey] = safeValue; | |
92 | } | |
93 | } | |
94 | return opentracing::make_expected(); | |
95 | }); | |
96 | ||
97 | if (!result && | |
98 | result.error() == opentracing::span_context_corrupted_error) { | |
99 | _metrics->decodingErrors().inc(1); | |
100 | return SpanContext(); | |
101 | } | |
102 | ||
103 | if (!ctx.traceID().isValid() && debugID.empty() && baggage.empty()) { | |
104 | return SpanContext(); | |
105 | } | |
106 | ||
107 | int flags = ctx.flags(); | |
108 | if (!debugID.empty()) { | |
109 | flags |= static_cast<unsigned char>(SpanContext::Flag::kDebug) | | |
110 | static_cast<unsigned char>(SpanContext::Flag::kSampled); | |
111 | } | |
112 | return SpanContext(ctx.traceID(), | |
113 | ctx.spanID(), | |
114 | ctx.parentID(), | |
115 | flags, | |
116 | baggage, | |
117 | debugID); | |
118 | } | |
119 | ||
120 | void inject(const SpanContext& ctx, const Writer& writer) const override | |
121 | { | |
122 | std::ostringstream oss; | |
123 | oss << ctx; | |
124 | writer.Set(_headerKeys.traceContextHeaderName(), oss.str()); | |
125 | ctx.forEachBaggageItem( | |
126 | [this, &writer](const std::string& key, const std::string& value) { | |
127 | const auto safeKey = addBaggageKeyPrefix(key); | |
128 | const auto safeValue = encodeValue(value); | |
129 | writer.Set(safeKey, safeValue); | |
130 | return true; | |
131 | }); | |
132 | } | |
133 | ||
134 | protected: | |
135 | virtual std::string encodeValue(const std::string& str) const | |
136 | { | |
137 | return str; | |
138 | } | |
139 | ||
140 | virtual std::string decodeValue(const std::string& str) const | |
141 | { | |
142 | return str; | |
143 | } | |
144 | ||
145 | virtual std::string normalizeKey(const std::string& rawKey) const | |
146 | { | |
147 | return rawKey; | |
148 | } | |
149 | ||
150 | private: | |
151 | static StrMap parseCommaSeparatedMap(const std::string& escapedValue) | |
152 | { | |
153 | StrMap map; | |
154 | std::istringstream iss(net::URI::queryUnescape(escapedValue)); | |
155 | std::string piece; | |
156 | while (std::getline(iss, piece, ',')) { | |
157 | const auto eqPos = piece.find('='); | |
158 | if (eqPos != std::string::npos) { | |
159 | const auto key = piece.substr(0, eqPos); | |
160 | const auto value = piece.substr(eqPos + 1); | |
161 | map[key] = value; | |
162 | } | |
163 | } | |
164 | return map; | |
165 | } | |
166 | ||
167 | std::string addBaggageKeyPrefix(const std::string& key) const | |
168 | { | |
169 | return _headerKeys.traceBaggageHeaderPrefix() + key; | |
170 | } | |
171 | ||
172 | std::string removeBaggageKeyPrefix(const std::string& key) const | |
173 | { | |
174 | return key.substr(_headerKeys.traceBaggageHeaderPrefix().size()); | |
175 | } | |
176 | ||
177 | HeadersConfig _headerKeys; | |
178 | std::shared_ptr<metrics::Metrics> _metrics; | |
179 | }; | |
180 | ||
181 | using TextMapPropagator = Propagator<const opentracing::TextMapReader&, | |
182 | const opentracing::TextMapWriter&>; | |
183 | ||
184 | class HTTPHeaderPropagator | |
185 | : public Propagator<const opentracing::HTTPHeadersReader&, | |
186 | const opentracing::HTTPHeadersWriter&> { | |
187 | public: | |
188 | using Propagator<Reader, Writer>::Propagator; | |
189 | ||
190 | protected: | |
191 | std::string encodeValue(const std::string& str) const override | |
192 | { | |
193 | return net::URI::queryEscape(str); | |
194 | } | |
195 | ||
196 | std::string decodeValue(const std::string& str) const override | |
197 | { | |
198 | return net::URI::queryUnescape(str); | |
199 | } | |
200 | ||
201 | std::string normalizeKey(const std::string& rawKey) const override | |
202 | { | |
203 | std::string key; | |
204 | key.reserve(rawKey.size()); | |
205 | std::transform(std::begin(rawKey), | |
206 | std::end(rawKey), | |
207 | std::back_inserter(key), | |
208 | [](char ch) { return std::tolower(ch); }); | |
209 | return key; | |
210 | } | |
211 | }; | |
212 | ||
213 | class BinaryPropagator : public Extractor<std::istream&>, | |
214 | public Injector<std::ostream&> { | |
215 | public: | |
216 | using StrMap = SpanContext::StrMap; | |
217 | ||
218 | explicit BinaryPropagator(const std::shared_ptr<metrics::Metrics>& metrics = | |
219 | std::shared_ptr<metrics::Metrics>()) | |
220 | : _metrics(metrics == nullptr ? metrics::Metrics::makeNullMetrics() | |
221 | : metrics) | |
222 | { | |
223 | } | |
224 | ||
225 | void inject(const SpanContext& ctx, std::ostream& out) const override | |
226 | { | |
227 | writeBinary(out, ctx.traceID().high()); | |
228 | writeBinary(out, ctx.traceID().low()); | |
229 | writeBinary(out, ctx.spanID()); | |
230 | writeBinary(out, ctx.parentID()); | |
231 | // `flags` is a single byte, so endianness is not an issue. | |
232 | out.put(ctx.flags()); | |
233 | ||
234 | writeBinary(out, static_cast<uint32_t>(ctx.baggage().size())); | |
235 | for (auto&& pair : ctx.baggage()) { | |
236 | auto&& key = pair.first; | |
237 | writeBinary(out, static_cast<uint32_t>(key.size())); | |
238 | out.write(key.c_str(), key.size()); | |
239 | ||
240 | auto&& value = pair.second; | |
241 | writeBinary(out, static_cast<uint32_t>(value.size())); | |
242 | out.write(value.c_str(), value.size()); | |
243 | } | |
244 | } | |
245 | ||
246 | SpanContext extract(std::istream& in) const override | |
247 | { | |
248 | const auto traceIDHigh = readBinary<uint64_t>(in); | |
249 | const auto traceIDLow = readBinary<uint64_t>(in); | |
250 | TraceID traceID(traceIDHigh, traceIDLow); | |
251 | const auto spanID = readBinary<uint64_t>(in); | |
252 | const auto parentID = readBinary<uint64_t>(in); | |
253 | ||
254 | auto ch = '\0'; | |
255 | in.get(ch); | |
256 | const auto flags = static_cast<unsigned char>(ch); | |
257 | ||
258 | const auto numBaggageItems = readBinary<uint32_t>(in); | |
259 | StrMap baggage; | |
260 | baggage.reserve(numBaggageItems); | |
261 | for (auto i = static_cast<uint32_t>(0); i < numBaggageItems; ++i) { | |
262 | const auto keyLength = readBinary<uint32_t>(in); | |
263 | std::string key(keyLength, '\0'); | |
264 | if (!in.read(&key[0], keyLength)) { | |
265 | _metrics->decodingErrors().inc(1); | |
266 | return SpanContext(); | |
267 | } | |
268 | ||
269 | const auto valueLength = readBinary<uint32_t>(in); | |
270 | std::string value(valueLength, '\0'); | |
271 | if (!in.read(&value[0], valueLength)) { | |
272 | _metrics->decodingErrors().inc(1); | |
273 | return SpanContext(); | |
274 | } | |
275 | ||
276 | baggage[key] = value; | |
277 | } | |
278 | ||
279 | SpanContext ctx(traceID, spanID, parentID, flags, baggage); | |
280 | return ctx; | |
281 | } | |
282 | ||
283 | private: | |
284 | template <typename ValueType> | |
285 | static | |
286 | typename std::enable_if<std::is_integral<ValueType>::value, void>::type | |
287 | writeBinary(std::ostream& out, ValueType value) | |
288 | { | |
289 | const ValueType outValue = platform::endian::toBigEndian(value); | |
290 | for (auto i = static_cast<size_t>(0); i < sizeof(ValueType); ++i) { | |
291 | const auto numShiftBits = (sizeof(ValueType) - i - 1) * CHAR_BIT; | |
292 | const auto byte = outValue >> numShiftBits; | |
293 | out.put(static_cast<unsigned char>(byte)); | |
294 | } | |
295 | } | |
296 | ||
297 | template <typename ValueType> | |
298 | static typename std::enable_if<std::is_integral<ValueType>::value, | |
299 | ValueType>::type | |
300 | readBinary(std::istream& in) | |
301 | { | |
302 | auto value = static_cast<ValueType>(0); | |
303 | auto ch = '\0'; | |
304 | for (auto i = static_cast<size_t>(0); | |
305 | i < sizeof(ValueType) && in.get(ch); | |
306 | ++i) { | |
307 | const auto byte = static_cast<uint8_t>(ch); | |
308 | value <<= CHAR_BIT; | |
309 | value |= byte; | |
310 | } | |
311 | return platform::endian::fromBigEndian(value); | |
312 | } | |
313 | ||
314 | private: | |
315 | std::shared_ptr<metrics::Metrics> _metrics; | |
316 | }; | |
317 | ||
318 | } // namespace propagation | |
319 | } // namespace jaegertracing | |
320 | ||
321 | #endif // JAEGERTRACING_PROPAGATION_PROPAGATOR_H |