/src/pdns/pdns/dnsdistdist/protozero-trace.hh
Line | Count | Source |
1 | | /* |
2 | | * This file is part of PowerDNS or dnsdist. |
3 | | * Copyright -- PowerDNS.COM B.V. and its contributors |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of version 2 of the GNU General Public License as |
7 | | * published by the Free Software Foundation. |
8 | | * |
9 | | * In addition, for the avoidance of any doubt, permission is granted to |
10 | | * link this program with OpenSSL and to (re)distribute the binaries |
11 | | * produced as the result of such linking. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
21 | | */ |
22 | | |
23 | | #pragma once |
24 | | |
25 | | #include <array> |
26 | | #include <variant> |
27 | | #include <vector> |
28 | | |
29 | | #include <protozero/pbf_reader.hpp> |
30 | | #include <protozero/pbf_writer.hpp> |
31 | | |
32 | | #include "dns_random.hh" |
33 | | #include "ednsoptions.hh" |
34 | | #include "misc.hh" |
35 | | |
36 | | // See https://github.com/open-telemetry/opentelemetry-proto/tree/main/opentelemetry/proto |
37 | | |
38 | | namespace pdns::trace |
39 | | { |
40 | | |
41 | | // https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/common/v1/common.proto |
42 | | |
43 | | struct AnyValue; |
44 | | struct ArrayValue; |
45 | | struct KeyValue; |
46 | | struct KeyValueList; |
47 | | |
48 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, bool value, bool always = false) |
49 | 0 | { |
50 | 0 | if (always || value) { |
51 | 0 | writer.add_bool(field, value); |
52 | 0 | } |
53 | 0 | } |
54 | | |
55 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, uint32_t value, bool always = false) |
56 | 0 | { |
57 | 0 | if (always || value != 0) { |
58 | 0 | writer.add_uint32(field, value); |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | inline void encodeFixed(protozero::pbf_writer& writer, uint8_t field, uint32_t value) |
63 | 0 | { |
64 | 0 | if (value != 0) { |
65 | 0 | writer.add_fixed32(field, value); |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, int64_t value, bool always = false) |
70 | 0 | { |
71 | 0 | if (always || value != 0) { |
72 | 0 | writer.add_int64(field, value); |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, uint64_t value, bool always = false) |
77 | 0 | { |
78 | 0 | if (always || value != 0) { |
79 | 0 | writer.add_uint64(field, value); |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | | inline void encodeFixed(protozero::pbf_writer& writer, uint8_t field, uint64_t value) |
84 | 0 | { |
85 | 0 | if (value != 0) { |
86 | 0 | writer.add_fixed64(field, value); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, double value, bool always = false) |
91 | 0 | { |
92 | 0 | if (always || value != 0.0) { |
93 | 0 | writer.add_double(field, value); |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, const std::string& value, bool always = false) |
98 | 0 | { |
99 | 0 | if (always || !value.empty()) { |
100 | 0 | writer.add_string(field, value); |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, const std::vector<uint8_t>& value, bool always = false) |
105 | 0 | { |
106 | 0 | if (always || !value.empty()) { |
107 | 0 | writer.add_bytes(field, reinterpret_cast<const char*>(value.data()), value.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) it's the API |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | template <typename T> |
112 | | void encode(protozero::pbf_writer& writer, const std::vector<T>& vec) |
113 | | { |
114 | | for (auto const& element : vec) { |
115 | | element.encode(writer); |
116 | | } |
117 | | } |
118 | | |
119 | | template <typename T> |
120 | | void encode(protozero::pbf_writer& writer, uint8_t field, const std::vector<T>& vec) |
121 | 0 | { |
122 | 0 | for (auto const& element : vec) { |
123 | 0 | protozero::pbf_writer sub{writer, field}; |
124 | 0 | element.encode(sub); |
125 | 0 | } |
126 | 0 | } Unexecuted instantiation: void pdns::trace::encode<pdns::trace::AnyValue>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::AnyValue, std::__1::allocator<pdns::trace::AnyValue> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::KeyValue>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::KeyValue, std::__1::allocator<pdns::trace::KeyValue> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::EntityRef>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::EntityRef, std::__1::allocator<pdns::trace::EntityRef> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::Span::Event>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::Span::Event, std::__1::allocator<pdns::trace::Span::Event> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::Span::Link>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::Span::Link, std::__1::allocator<pdns::trace::Span::Link> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::Span>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::Span, std::__1::allocator<pdns::trace::Span> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::ScopeSpans>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::ScopeSpans, std::__1::allocator<pdns::trace::ScopeSpans> > const&) Unexecuted instantiation: void pdns::trace::encode<pdns::trace::ResourceSpans>(protozero::basic_pbf_writer<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >&, unsigned char, std::__1::vector<pdns::trace::ResourceSpans, std::__1::allocator<pdns::trace::ResourceSpans> > const&) |
127 | | |
128 | | template <typename T, typename E> |
129 | | T decode(protozero::pbf_reader& reader) |
130 | 0 | { |
131 | 0 | std::vector<E> vec; |
132 | 0 | while (reader.next()) { |
133 | 0 | if (reader.tag() == 1) { |
134 | 0 | protozero::pbf_reader sub = reader.get_message(); |
135 | 0 | vec.emplace_back(E::decode(sub)); |
136 | 0 | } |
137 | 0 | } |
138 | 0 | return {std::move(vec)}; |
139 | 0 | } Unexecuted instantiation: pdns::trace::ArrayValue pdns::trace::decode<pdns::trace::ArrayValue, pdns::trace::AnyValue>(protozero::pbf_reader&) Unexecuted instantiation: pdns::trace::KeyValueList pdns::trace::decode<pdns::trace::KeyValueList, pdns::trace::KeyValue>(protozero::pbf_reader&) |
140 | | |
141 | | struct ArrayValue |
142 | | { |
143 | | std::vector<AnyValue> values; // = 1 |
144 | | |
145 | | void encode(protozero::pbf_writer& writer) const |
146 | 0 | { |
147 | 0 | pdns::trace::encode(writer, 1, values); |
148 | 0 | } |
149 | | |
150 | | static ArrayValue decode(protozero::pbf_reader& reader); |
151 | | |
152 | | bool operator==(const ArrayValue& rhs) const; |
153 | | }; |
154 | | |
155 | | struct KeyValueList |
156 | | { |
157 | | std::vector<KeyValue> values; // = 1 |
158 | | |
159 | | void encode(protozero::pbf_writer& writer) const |
160 | 0 | { |
161 | 0 | pdns::trace::encode(writer, 1, values); |
162 | 0 | } |
163 | | |
164 | | static KeyValueList decode(protozero::pbf_reader& reader); |
165 | | |
166 | | bool operator==(const KeyValueList& rhs) const; |
167 | | }; |
168 | | |
169 | | using NoValue = char; |
170 | | struct AnyValue : public std::variant<NoValue, std::string, bool, int64_t, double, ArrayValue, KeyValueList, std::vector<uint8_t>> |
171 | | { |
172 | | void encode(protozero::pbf_writer& writer) const; |
173 | | static AnyValue decode(protozero::pbf_reader& reader); |
174 | | [[nodiscard]] std::string toLogString() const; |
175 | | friend std::ostream& operator<<(std::ostream& ostrm, const AnyValue& val) |
176 | 0 | { |
177 | 0 | return ostrm << val.toLogString(); |
178 | 0 | } |
179 | | }; |
180 | | |
181 | | struct EntityRef |
182 | | { |
183 | | std::string schema_url{}; // == 1 |
184 | | std::string type{}; // == 2 |
185 | | std::vector<std::string> id_keys{}; // == 3 |
186 | | std::vector<std::string> description_keys{}; // == 4 |
187 | | |
188 | | void encode(protozero::pbf_writer& writer) const; |
189 | | static EntityRef decode(protozero::pbf_reader& reader); |
190 | | bool operator==(const EntityRef& rhs) const; |
191 | | }; |
192 | | |
193 | | struct KeyValue |
194 | | { |
195 | | std::string key{}; // = 1 |
196 | | AnyValue value{}; // = 2 |
197 | | void encode(protozero::pbf_writer& writer) const; |
198 | | static KeyValue decode(protozero::pbf_reader& reader); |
199 | | |
200 | | bool operator==(const KeyValue& rhs) const; |
201 | | }; |
202 | | |
203 | | struct Resource |
204 | | { |
205 | | std::vector<KeyValue> attributes{}; // = 1 |
206 | | uint32_t dropped_attributes_count{0}; // = 2; |
207 | | std::vector<EntityRef> entity_refs{}; // = 3 |
208 | | |
209 | | void encode(protozero::pbf_writer& writer) const; |
210 | | static Resource decode(protozero::pbf_reader& reader); |
211 | | bool operator==(const Resource& rhs) const; |
212 | | }; |
213 | | |
214 | | struct InstrumentationScope |
215 | | { |
216 | | std::string name{}; // = 1 |
217 | | std::string version{}; // = 2 |
218 | | std::vector<KeyValue> attributes{}; // = 3 |
219 | | uint32_t dropped_attributes_count{0}; // = 4 |
220 | | |
221 | | void encode(protozero::pbf_writer& writer) const; |
222 | | static InstrumentationScope decode(protozero::pbf_reader& reader); |
223 | | bool operator==(const InstrumentationScope& rhs) const; |
224 | | }; |
225 | | |
226 | | struct TraceID : public std::array<uint8_t, 16> |
227 | | { |
228 | | constexpr TraceID() : |
229 | 0 | array{} {}; |
230 | | TraceID(const std::initializer_list<uint8_t>& arg) : |
231 | | array{} |
232 | 0 | { |
233 | 0 | std::copy(arg.begin(), arg.end(), begin()); |
234 | 0 | } |
235 | | |
236 | | [[nodiscard]] std::string toLogString() const; |
237 | | friend std::ostream& operator<<(std::ostream& ostrm, const TraceID& val) |
238 | 0 | { |
239 | 0 | return ostrm << val.toLogString(); |
240 | 0 | } |
241 | | |
242 | | static TraceID getRandomTraceID() |
243 | 0 | { |
244 | 0 | TraceID ret; |
245 | 0 | ret.makeRandom(); |
246 | 0 | return ret; |
247 | 0 | } |
248 | | |
249 | | void makeRandom() |
250 | 0 | { |
251 | 0 | dns_random(this->data(), this->size()); |
252 | 0 | } |
253 | | |
254 | | void clear() |
255 | 0 | { |
256 | 0 | this->fill(0); |
257 | 0 | } |
258 | | }; |
259 | | constexpr TraceID s_emptyTraceID = {}; |
260 | | |
261 | | struct SpanID : public std::array<uint8_t, 8> |
262 | | { |
263 | | constexpr SpanID() : |
264 | 0 | array{} {}; |
265 | | SpanID(const std::initializer_list<uint8_t>& arg) : |
266 | | array{} |
267 | 0 | { |
268 | 0 | std::copy(arg.begin(), arg.end(), begin()); |
269 | 0 | } |
270 | | |
271 | | [[nodiscard]] std::string toLogString() const; |
272 | | friend std::ostream& operator<<(std::ostream& ostrm, const SpanID& val) |
273 | 0 | { |
274 | 0 | return ostrm << val.toLogString(); |
275 | 0 | } |
276 | | |
277 | | static SpanID getRandomSpanID() |
278 | 0 | { |
279 | 0 | SpanID ret; |
280 | 0 | ret.makeRandom(); |
281 | 0 | return ret; |
282 | 0 | } |
283 | | |
284 | | void makeRandom() |
285 | 0 | { |
286 | 0 | dns_random(this->data(), this->size()); |
287 | 0 | } |
288 | | |
289 | | void clear() |
290 | 0 | { |
291 | 0 | this->fill(0); |
292 | 0 | } |
293 | | }; |
294 | | constexpr SpanID s_emptySpanID = {}; |
295 | | |
296 | | inline void fill(TraceID& trace, const std::string& data) |
297 | 0 | { |
298 | 0 | if (data.size() != trace.size()) { |
299 | 0 | throw std::runtime_error("TraceID size mismatch"); |
300 | 0 | } |
301 | 0 | std::copy(data.begin(), data.end(), trace.begin()); |
302 | 0 | } |
303 | | |
304 | | inline void fill(SpanID& span, const std::string& data) |
305 | 0 | { |
306 | 0 | if (data.size() != span.size()) { |
307 | 0 | throw std::runtime_error("SpanID size mismatch"); |
308 | 0 | } |
309 | 0 | std::copy(data.begin(), data.end(), span.begin()); |
310 | 0 | } |
311 | | |
312 | | inline void fill(TraceID& trace, const char* data, size_t size) |
313 | 0 | { |
314 | 0 | fill(trace, std::string(data, size)); |
315 | 0 | } |
316 | | |
317 | | inline void fill(SpanID& span, const char* data, size_t size) |
318 | 0 | { |
319 | 0 | fill(span, std::string(data, size)); |
320 | 0 | } |
321 | | |
322 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, const TraceID& value) |
323 | 0 | { |
324 | 0 | writer.add_bytes(field, reinterpret_cast<const char*>(value.data()), value.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) it's the API |
325 | 0 | } |
326 | | |
327 | | inline TraceID decodeTraceID(protozero::pbf_reader& reader) |
328 | 0 | { |
329 | 0 | TraceID bytes; |
330 | 0 | const auto data = reader.get_view(); |
331 | 0 | const auto len = std::min(bytes.size(), data.size()); |
332 | 0 | std::copy(data.data(), data.data() + len, bytes.begin()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
333 | 0 | return bytes; |
334 | 0 | } |
335 | | |
336 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, const SpanID& value) |
337 | 0 | { |
338 | 0 | writer.add_bytes(field, reinterpret_cast<const char*>(value.data()), value.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) it's the API |
339 | 0 | } |
340 | | |
341 | | inline SpanID decodeSpanID(protozero::pbf_reader& reader) |
342 | 0 | { |
343 | 0 | SpanID bytes; |
344 | 0 | const auto data = reader.get_view(); |
345 | 0 | const auto len = std::min(bytes.size(), data.size()); |
346 | 0 | std::copy(data.data(), data.data() + len, bytes.begin()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
347 | 0 | return bytes; |
348 | 0 | } |
349 | | |
350 | | // The Status type defines a logical error model that is suitable for different |
351 | | // programming environments, including REST APIs and RPC APIs. |
352 | | struct Status |
353 | | { |
354 | | // A developer-facing human readable error message. |
355 | | std::string message{}; // = 2; |
356 | | |
357 | | // For the semantics of status codes see |
358 | | // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status |
359 | | enum class StatusCode : uint8_t |
360 | | { |
361 | | // The default status. |
362 | | STATUS_CODE_UNSET = 0, |
363 | | // The Span has been validated by an Application developer or Operator to |
364 | | // have completed successfully. |
365 | | STATUS_CODE_OK = 1, |
366 | | // The Span contains an error. |
367 | | STATUS_CODE_ERROR = 2, |
368 | | }; |
369 | | |
370 | | // The status code. |
371 | | StatusCode code{StatusCode::STATUS_CODE_UNSET}; // = 3; |
372 | | |
373 | | void clear() |
374 | 0 | { |
375 | 0 | message.clear(); |
376 | 0 | code = StatusCode::STATUS_CODE_UNSET; |
377 | 0 | } |
378 | | void encode(protozero::pbf_writer& writer) const; |
379 | | static Status decode(protozero::pbf_reader& reader); |
380 | | bool operator==(const Status& rhs) const; |
381 | | }; |
382 | | |
383 | | inline uint64_t timestamp() |
384 | 0 | { |
385 | 0 | timespec now{}; |
386 | 0 | clock_gettime(CLOCK_REALTIME, &now); |
387 | 0 | return (1000000000ULL * now.tv_sec) + now.tv_nsec; |
388 | 0 | } |
389 | | |
390 | | // This struct is used to store the info of the initial span. As it is passed around resolving |
391 | | // queries, it needs to be as small as possible, hence no full Span. |
392 | | struct InitialSpanInfo |
393 | | { |
394 | | TraceID trace_id{}; |
395 | | SpanID span_id{}; |
396 | | SpanID parent_span_id{}; |
397 | | uint64_t start_time_unix_nano{0}; |
398 | | |
399 | | void clear() |
400 | 0 | { |
401 | 0 | trace_id.clear(); |
402 | 0 | span_id.clear(); |
403 | 0 | parent_span_id.clear(); |
404 | 0 | start_time_unix_nano = 0; |
405 | 0 | } |
406 | | |
407 | | void setIDsIfNotSet() |
408 | 0 | { |
409 | 0 | if (trace_id == s_emptyTraceID) { |
410 | 0 | trace_id.makeRandom(); |
411 | 0 | } |
412 | 0 | if (span_id == s_emptySpanID) { |
413 | 0 | span_id.makeRandom(); |
414 | 0 | } |
415 | 0 | } |
416 | | }; |
417 | | |
418 | | struct Span |
419 | | { |
420 | | // A unique identifier for a trace. All spans from the same trace share |
421 | | // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR |
422 | | // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON |
423 | | // is zero-length and thus is also invalid). |
424 | | // |
425 | | // This field is required. |
426 | | TraceID trace_id{}; // = 1 |
427 | | // A unique identifier for a span within a trace, assigned when the span |
428 | | // is created. The ID is an 8-byte array. An ID with all zeroes OR of length |
429 | | // other than 8 bytes is considered invalid (empty string in OTLP/JSON |
430 | | // is zero-length and thus is also invalid). |
431 | | // |
432 | | // This field is required. |
433 | | SpanID span_id{}; // = 2 |
434 | | // trace_state conveys information about request position in multiple distributed tracing graphs. |
435 | | // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header |
436 | | // See also https://github.com/w3c/distributed-tracing for more details about this field. |
437 | | std::string trace_state{}; // = 3 |
438 | | // The `span_id` of this span's parent span. If this is a root span, then this |
439 | | // field must be empty. The ID is an 8-byte array. |
440 | | SpanID parent_span_id{}; // = 4 |
441 | | // A description of the span's operation. |
442 | | // |
443 | | // For example, the name can be a qualified method name or a file name |
444 | | // and a line number where the operation is called. A best practice is to use |
445 | | // the same display name at the same call point in an application. |
446 | | // This makes it easier to correlate spans in different traces. |
447 | | // |
448 | | // This field is semantically required to be set to non-empty string. |
449 | | // Empty value is equivalent to an unknown span name. |
450 | | // |
451 | | // This field is required. |
452 | | std::string name{}; // = 5 |
453 | | |
454 | | // SpanKind is the type of span. Can be used to specify additional relationships between spans |
455 | | // in addition to a parent/child relationship. |
456 | | enum class SpanKind : uint8_t |
457 | | { |
458 | | // Unspecified. Do NOT use as default. |
459 | | // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. |
460 | | SPAN_KIND_UNSPECIFIED = 0, |
461 | | // Indicates that the span represents an internal operation within an application, |
462 | | // as opposed to an operation happening at the boundaries. Default value. |
463 | | SPAN_KIND_INTERNAL = 1, |
464 | | // Indicates that the span covers server-side handling of an RPC or other |
465 | | // remote network request. |
466 | | SPAN_KIND_SERVER = 2, |
467 | | // Indicates that the span describes a request to some remote service. |
468 | | SPAN_KIND_CLIENT = 3, |
469 | | // Indicates that the span describes a producer sending a message to a broker. |
470 | | // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship |
471 | | // between producer and consumer spans. A PRODUCER span ends when the message was accepted |
472 | | // by the broker while the logical processing of the message might span a much longer time. |
473 | | SPAN_KIND_PRODUCER = 4, |
474 | | // Indicates that the span describes consumer receiving a message from a broker. |
475 | | // Like the PRODUCER kind, there is often no direct critical path latency relationship |
476 | | // between producer and consumer spans. |
477 | | SPAN_KINCONSUMER = 5, |
478 | | }; |
479 | | // Distinguishes between spans generated in a particular context. For example, |
480 | | // two spans with the same name may be distinguished using `CLIENT` (caller) |
481 | | // and `SERVER` (callee) to identify queueing latency associated with the span. |
482 | | SpanKind kind{Span::SpanKind::SPAN_KIND_UNSPECIFIED}; // = 6 |
483 | | // start_time_unix_nano is the start time of the span. On the client side, this is the time |
484 | | // kept by the local machine where the span execution starts. On the server side, this |
485 | | // is the time when the server's application handler starts running. |
486 | | // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. |
487 | | // |
488 | | // This field is semantically required and it is expected that end_time >= start_time. |
489 | | uint64_t start_time_unix_nano{0}; // = 7 |
490 | | // end_time_unix_nano is the end time of the span. On the client side, this is the time |
491 | | // kept by the local machine where the span execution ends. On the server side, this |
492 | | // is the time when the server application handler stops running. |
493 | | // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. |
494 | | // |
495 | | // This field is semantically required and it is expected that end_time >= start_time. |
496 | | uint64_t end_time_unix_nano{0}; // = 8 |
497 | | // attributes is a collection of key/value pairs. Note, global attributes |
498 | | // like server name can be set using the resource API. Examples of attributes: |
499 | | // |
500 | | // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" |
501 | | // "/http/server_latency": 300 |
502 | | // "example.com/myattribute": true |
503 | | // "example.com/score": 10.239 |
504 | | // |
505 | | // The OpenTelemetry API specification further restricts the allowed value types: |
506 | | // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute |
507 | | // Attribute keys MUST be unique (it is not allowed to have more than one |
508 | | // attribute with the same key). |
509 | | std::vector<KeyValue> attributes{}; // = 9 |
510 | | // dropped_attributes_count is the number of attributes that were discarded. Attributes |
511 | | // can be discarded because their keys are too long or because there are too many |
512 | | // attributes. If this value is 0, then no attributes were dropped. |
513 | | uint32_t dropped_attributes_count{0}; // = 10 |
514 | | |
515 | | // Event is a time-stamped annotation of the span, consisting of user-supplied |
516 | | // text description and key-value pairs.x |
517 | | struct Event |
518 | | { |
519 | | // time_unix_nano is the time the event occurred. |
520 | | uint64_t time_unix_nano; // = 1 |
521 | | // name of the event. |
522 | | // This field is semantically required to be set to non-empty string. |
523 | | std::string name; // = 2 |
524 | | // attributes is a collection of attribute key/value pairs on the event. |
525 | | // Attribute keys MUST be unique (it is not allowed to have more than one |
526 | | // attribute with the same key). |
527 | | std::vector<KeyValue> attributes; // = 3 |
528 | | // dropped_attributes_count is the number of dropped attributes. If the value is 0, |
529 | | // then no attributes were dropped. |
530 | | uint32_t dropped_attributes_count{0}; // = 4 |
531 | | |
532 | | void encode(protozero::pbf_writer& writer) const; |
533 | | static Event decode(protozero::pbf_reader& reader); |
534 | | bool operator==(const Span::Event& rhs) const; |
535 | | }; |
536 | | // events is a collection of Event items. |
537 | | std::vector<Event> events{}; // = 11 |
538 | | // dropped_events_count is the number of dropped events. If the value is 0, then no |
539 | | // events were dropped. |
540 | | uint32_t dropped_events_count{0}; // = 12 |
541 | | |
542 | | // A pointer from the current span to another span in the same trace or in a |
543 | | // different trace. For example, this can be used in batching operations, |
544 | | // where a single batch handler processes multiple requests from different |
545 | | // traces or when the handler receives a request from a different project. |
546 | | struct Link |
547 | | { |
548 | | // A unique identifier of a trace that this linked span is part of. The ID is a |
549 | | // 16-byte array. |
550 | | TraceID trace_id; // = 1 |
551 | | // A unique identifier for the linked span. The ID is an 8-byte array. |
552 | | SpanID span_id; // = 2 |
553 | | // The trace_state associated with the link. |
554 | | std::string trace_state; // = 3 |
555 | | // attributes is a collection of attribute key/value pairs on the link. |
556 | | // Attribute keys MUST be unique (it is not allowed to have more than one |
557 | | // attribute with the same key). |
558 | | std::vector<KeyValue> attributes; // = 4 |
559 | | // dropped_attributes_count is the number of dropped attributes. If the value is 0, |
560 | | // then no attributes were dropped. |
561 | | uint32_t dropped_attributes_count{0}; // = 5 |
562 | | // Flags, a bit field. |
563 | | // |
564 | | // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace |
565 | | // Context specification. To read the 8-bit W3C trace flag, use |
566 | | // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. |
567 | | // |
568 | | // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. |
569 | | // |
570 | | // Bits 8 and 9 represent the 3 states of whether the link is remote. |
571 | | // The states are (unknown, is not remote, is remote). |
572 | | // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. |
573 | | // To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. |
574 | | // |
575 | | // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. |
576 | | // When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. |
577 | | // |
578 | | // [Optional]. |
579 | | uint32_t flags{0}; // = 6 |
580 | | |
581 | | void encode(protozero::pbf_writer& writer) const; |
582 | | static Link decode(protozero::pbf_reader& reader); |
583 | | bool operator==(const Span::Link& rhs) const; |
584 | | }; |
585 | | std::vector<Link> links{}; // = 13 |
586 | | uint32_t dropped_links_count{0}; // = 14 |
587 | | Status status{}; // = 15 |
588 | | |
589 | | // Flags, a bit field. |
590 | | // |
591 | | // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace |
592 | | // Context specification. To read the 8-bit W3C trace flag, use |
593 | | // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. |
594 | | // |
595 | | // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. |
596 | | // |
597 | | // Bits 8 and 9 represent the 3 states of whether a span's parent |
598 | | // is remote. The states are (unknown, is not remote, is remote). |
599 | | // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. |
600 | | // To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. |
601 | | // |
602 | | // When creating span messages, if the message is logically forwarded from another source |
603 | | // with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD |
604 | | // be copied as-is. If creating from a source that does not have an equivalent flags field |
605 | | // (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST |
606 | | // be set to zero. |
607 | | // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. |
608 | | // |
609 | | // [Optional]. |
610 | | uint32_t flags{0}; // = 16; |
611 | | |
612 | | void close() |
613 | 0 | { |
614 | 0 | end_time_unix_nano = timestamp(); |
615 | 0 | } |
616 | | |
617 | | void clear() |
618 | 0 | { |
619 | 0 | trace_id.clear(); // 1 |
620 | 0 | span_id.clear(); // 2 |
621 | 0 | trace_state.clear(); // 3 |
622 | 0 | parent_span_id.clear(); // 4 |
623 | 0 | name.clear(); // 5 |
624 | 0 | kind = SpanKind::SPAN_KIND_UNSPECIFIED; // 6 |
625 | 0 | start_time_unix_nano = 0; // 7 |
626 | 0 | end_time_unix_nano = 0; // 8 |
627 | 0 | attributes.clear(); // 9 |
628 | 0 | dropped_attributes_count = 0; // 10 |
629 | 0 | events.clear(); // 11 |
630 | 0 | dropped_events_count = 0; // 12 |
631 | 0 | links.clear(); // 13 |
632 | 0 | dropped_links_count = 0; //14 |
633 | 0 | status.clear(); // 15 |
634 | 0 | flags = 0; // 16 |
635 | 0 | } |
636 | | void encode(protozero::pbf_writer& writer) const; |
637 | | static Span decode(protozero::pbf_reader& reader); |
638 | | bool operator==(const Span& rhs) const; |
639 | | }; |
640 | | |
641 | | // SpanFlags represents constants used to interpret the |
642 | | // Span.flags field, which is protobuf 'fixed32' type and is to |
643 | | // be used as bit-fields. Each non-zero value defined in this enum is |
644 | | // a bit-mask. To extract the bit-field, for example, use an |
645 | | // expression like: |
646 | | // |
647 | | // (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) |
648 | | // |
649 | | // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. |
650 | | // |
651 | | // Note that Span flags were introduced in version 1.1 of the |
652 | | // OpenTelemetry protocol. Older Span producers do not set this |
653 | | // field, consequently consumers should not rely on the absence of a |
654 | | // particular flag bit to indicate the presence of a particular feature. |
655 | | enum class SpanFlags : uint16_t |
656 | | { |
657 | | // The zero value for the enum. Should not be used for comparisons. |
658 | | // Instead use bitwise "and" with the appropriate mask as shown above. |
659 | | SPAN_FLAGS_DO_NOT_USE = 0, |
660 | | // Bits 0-7 are used for trace flags. |
661 | | SPAN_FLAGS_TRACE_FLAGS_MASK = 0x000000FF, |
662 | | // Bits 8 and 9 are used to indicate that the parent span or link span is remote. |
663 | | // Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. |
664 | | // Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. |
665 | | SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 0x00000100, |
666 | | SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 0x00000200, |
667 | | // Bits 10-31 are reserved for future use. |
668 | | }; |
669 | | |
670 | | // A collection of Spans produced by an InstrumentationScope. |
671 | | struct ScopeSpans |
672 | | { |
673 | | // The instrumentation scope information for the spans in this message. |
674 | | // Semantically when InstrumentationScope isn't set, it is equivalent with |
675 | | // an empty instrumentation scope name (unknown). |
676 | | InstrumentationScope scope{}; // = 1 |
677 | | // A list of Spans that originate from an instrumentation scope. |
678 | | std::vector<Span> spans{}; // = 2 |
679 | | // The Schema URL, if known. This is the identifier of the Schema that the span data |
680 | | // is recorded in. Notably, the last part of the URL path is the version number of the |
681 | | // schema: http[s]://server[:port]/path/<version>. To learn more about Schema URL see |
682 | | // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url |
683 | | // This schema_url applies to all spans and span events in the "spans" field. |
684 | | std::string schema_url{}; // = 3 |
685 | | |
686 | | void close() |
687 | 0 | { |
688 | 0 | for (auto& element : spans) { |
689 | 0 | element.close(); |
690 | 0 | } |
691 | 0 | } |
692 | | void encode(protozero::pbf_writer& writer) const; |
693 | | static ScopeSpans decode(protozero::pbf_reader& reader); |
694 | | bool operator==(const ScopeSpans& rhs) const; |
695 | | }; |
696 | | |
697 | | // A collection of ScopeSpans from a Resource. |
698 | | struct ResourceSpans |
699 | | { |
700 | | // The resource for the spans in this message. |
701 | | // If this field is not set then no resource info is known. |
702 | | Resource resource; // = 1 |
703 | | // A list of ScopeSpans that originate from a resource. |
704 | | std::vector<ScopeSpans> scope_spans; // = 2 |
705 | | // The Schema URL, if known. This is the identifier of the Schema that the resource data |
706 | | // is recorded in. Notably, the last part of the URL path is the version number of the |
707 | | // schema: http[s]://server[:port]/path/<version>. To learn more about Schema URL see |
708 | | // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url |
709 | | // This schema_url applies to the data in the "resource" field. It does not apply |
710 | | // to the data in the "scope_spans" field which have their own schema_url field. |
711 | | std::string schema_url{}; // = 3 |
712 | | |
713 | | void close() |
714 | 0 | { |
715 | 0 | for (auto& element : scope_spans) { |
716 | 0 | element.close(); |
717 | 0 | } |
718 | 0 | } |
719 | | void encode(protozero::pbf_writer& writer) const; |
720 | | static ResourceSpans decode(protozero::pbf_reader& reader); |
721 | | bool operator==(const ResourceSpans& rhs) const; |
722 | | }; |
723 | | |
724 | | // TracesData represents the traces data that can be stored in a persistent storage, |
725 | | // OR can be embedded by other protocols that transfer OTLP traces data but do |
726 | | // not implement the OTLP protocol. |
727 | | // |
728 | | // The main difference between this message and collector protocol is that |
729 | | // in this message there will not be any "control" or "metadata" specific to |
730 | | // OTLP protocol. |
731 | | // |
732 | | // When new fields are added into this message, the OTLP request MUST be updated |
733 | | // as well. |
734 | | struct TracesData |
735 | | { |
736 | | // An array of ResourceSpans. |
737 | | // For data coming from a single resource this array will typically contain |
738 | | // one element. Intermediary nodes that receive data from multiple origins |
739 | | // typically batch the data before forwarding further and in that case this |
740 | | // array will contain multiple elements. |
741 | | std::vector<ResourceSpans> resource_spans; // = 1 |
742 | | |
743 | | void close() |
744 | 0 | { |
745 | 0 | for (auto& element : resource_spans) { |
746 | 0 | element.close(); |
747 | 0 | } |
748 | 0 | } |
749 | | void encode(protozero::pbf_writer& writer) const; |
750 | | static TracesData decode(protozero::pbf_reader& reader); |
751 | | |
752 | | [[nodiscard]] std::string encode() const |
753 | 0 | { |
754 | 0 | std::string data; |
755 | 0 | protozero::pbf_writer writer{data}; |
756 | 0 | encode(writer); |
757 | 0 | return data; |
758 | 0 | } |
759 | | |
760 | | static TracesData boilerPlate(std::string&& service, std::vector<Span>&& spans, const std::vector<KeyValue>& attributes, const std::string& serverID) |
761 | 0 | { |
762 | 0 | auto& spanAttrs = spans.at(0).attributes; |
763 | 0 | spanAttrs.insert(spanAttrs.end(), attributes.begin(), attributes.end()); |
764 | 0 | InstrumentationScope scope{ |
765 | 0 | .name = "rec", .version = VERSION}; |
766 | 0 | return TracesData{ |
767 | 0 | .resource_spans = {pdns::trace::ResourceSpans{ |
768 | 0 | .resource = { |
769 | 0 | .attributes = { |
770 | 0 | {"service.name", {{std::move(service)}}}, |
771 | 0 | {"instance", {serverID}}}}, |
772 | 0 | .scope_spans = {{.scope = std::move(scope), .spans = std::move(spans)}}}}}; |
773 | 0 | } |
774 | | }; |
775 | | |
776 | | inline ArrayValue ArrayValue::decode(protozero::pbf_reader& reader) |
777 | 0 | { |
778 | 0 | return pdns::trace::decode<ArrayValue, AnyValue>(reader); |
779 | 0 | } |
780 | | |
781 | | inline KeyValueList KeyValueList::decode(protozero::pbf_reader& reader) |
782 | 0 | { |
783 | 0 | return pdns::trace::decode<KeyValueList, KeyValue>(reader); |
784 | 0 | } |
785 | | |
786 | | struct EDNSOTTraceRecord |
787 | | { |
788 | | // 1 byte version, 1 byte reserved, 16 bytes traceid, 8 bytes spanid, 1 byte flags |
789 | | static constexpr size_t fullSize = 1 + 1 + 16 + 8 + 1; |
790 | | static constexpr size_t traceIDOffset = 1 + 1; |
791 | | static constexpr size_t spanIDOffset = traceIDOffset + 16; |
792 | | static constexpr size_t flagsOffset = spanIDOffset + 8; |
793 | | |
794 | | EDNSOTTraceRecord(uint8_t* arg) : |
795 | 0 | data(arg) {} |
796 | | // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
797 | | void setVersion(uint8_t version) |
798 | 0 | { |
799 | 0 | data[0] = version; |
800 | 0 | } |
801 | | void setReserved(uint8_t reserved) |
802 | 0 | { |
803 | 0 | data[1] = reserved; |
804 | 0 | } |
805 | | void setTraceID(const TraceID& traceid) |
806 | 0 | { |
807 | 0 | std::copy(traceid.begin(), traceid.end(), &data[traceIDOffset]); |
808 | 0 | } |
809 | | void setSpanID(const SpanID& spanid) |
810 | 0 | { |
811 | 0 | std::copy(spanid.begin(), spanid.end(), &data[spanIDOffset]); |
812 | 0 | } |
813 | | void setFlags(const uint8_t flags) |
814 | 0 | { |
815 | 0 | data[flagsOffset] = flags; |
816 | 0 | } |
817 | | // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
818 | | private: |
819 | | uint8_t* data; |
820 | | }; |
821 | | |
822 | | struct EDNSOTTraceRecordView |
823 | | { |
824 | | EDNSOTTraceRecordView(const uint8_t* arg, size_t argsize) : |
825 | 0 | data(arg), size(argsize) {} |
826 | | |
827 | | // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
828 | | [[nodiscard]] bool getVersion(uint8_t& version) const |
829 | 0 | { |
830 | 0 | if (size > 0) { |
831 | 0 | version = data[0]; |
832 | 0 | return true; |
833 | 0 | } |
834 | 0 | return false; |
835 | 0 | } |
836 | | [[nodiscard]] bool getReserved(uint8_t& reserved) const |
837 | 0 | { |
838 | 0 | if (size > 1) { |
839 | 0 | reserved = data[1]; |
840 | 0 | return true; |
841 | 0 | } |
842 | 0 | return false; |
843 | 0 | } |
844 | | [[nodiscard]] bool getTraceID(TraceID& traceid) const |
845 | 0 | { |
846 | 0 | if (size == pdns::trace::EDNSOTTraceRecord::fullSize) { |
847 | 0 | std::copy(&data[EDNSOTTraceRecord::traceIDOffset], &data[EDNSOTTraceRecord::traceIDOffset + traceid.size()], traceid.begin()); |
848 | 0 | return true; |
849 | 0 | } |
850 | 0 | return false; |
851 | 0 | } |
852 | | [[nodiscard]] bool getSpanID(SpanID& spanid) const |
853 | 0 | { |
854 | 0 | if (size == pdns::trace::EDNSOTTraceRecord::fullSize) { |
855 | 0 | std::string x((char*)&data[EDNSOTTraceRecord::spanIDOffset], spanid.size()); |
856 | 0 | std::copy(&data[EDNSOTTraceRecord::spanIDOffset], &data[EDNSOTTraceRecord::spanIDOffset + spanid.size()], spanid.begin()); |
857 | 0 | return true; |
858 | 0 | } |
859 | 0 | return false; |
860 | 0 | } |
861 | | [[nodiscard]] bool getFlags(uint8_t& flags) const |
862 | 0 | { |
863 | 0 | if (size == pdns::trace::EDNSOTTraceRecord::fullSize) { |
864 | 0 | flags = data[EDNSOTTraceRecord::flagsOffset]; |
865 | 0 | return true; |
866 | 0 | } |
867 | 0 | return false; |
868 | 0 | } |
869 | | // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
870 | | private: |
871 | | const uint8_t* const data; |
872 | | const size_t size; |
873 | | }; |
874 | | |
875 | | bool extractOTraceIDs(const EDNSOptionViewMap& map, EDNSOptionCode::EDNSOptionCodeEnum eoc, pdns::trace::InitialSpanInfo& span); |
876 | | bool extractOTraceIDs(const EDNSOptionViewMap& map, EDNSOptionCode::EDNSOptionCodeEnum eoc, pdns::trace::TraceID& traceID, pdns::trace::SpanID& spanID); |
877 | | |
878 | | inline bool ArrayValue::operator==(const ArrayValue& rhs) const |
879 | 0 | { |
880 | 0 | return values == rhs.values; |
881 | 0 | } |
882 | | |
883 | | inline bool KeyValueList::operator==(const KeyValueList& rhs) const |
884 | 0 | { |
885 | 0 | return values == rhs.values; |
886 | 0 | } |
887 | | |
888 | | inline bool KeyValue::operator==(const KeyValue& rhs) const |
889 | 0 | { |
890 | 0 | return key == rhs.key && value == rhs.value; |
891 | 0 | } |
892 | | |
893 | | } // namespace pdns::trace |