/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&) |
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 | 0 | { |
154 | 0 | return values == rhs.values; |
155 | 0 | } |
156 | | }; |
157 | | |
158 | | struct KeyValueList |
159 | | { |
160 | | std::vector<KeyValue> values{}; // = 1 |
161 | | |
162 | | void encode(protozero::pbf_writer& writer) const |
163 | 0 | { |
164 | 0 | pdns::trace::encode(writer, 1, values); |
165 | 0 | } |
166 | | |
167 | | static KeyValueList decode(protozero::pbf_reader& reader); |
168 | | |
169 | | bool operator==(const KeyValueList& rhs) const |
170 | 0 | { |
171 | 0 | return values == rhs.values; |
172 | 0 | } |
173 | | }; |
174 | | |
175 | | using NoValue = char; |
176 | | struct AnyValue : public std::variant<NoValue, std::string, bool, int64_t, double, ArrayValue, KeyValueList, std::vector<uint8_t>> |
177 | | { |
178 | | void encode(protozero::pbf_writer& writer) const; |
179 | | static AnyValue decode(protozero::pbf_reader& reader); |
180 | | [[nodiscard]] std::string toLogString() const; |
181 | | friend std::ostream& operator<<(std::ostream& ostrm, const AnyValue& val) |
182 | 0 | { |
183 | 0 | return ostrm << val.toLogString(); |
184 | 0 | } |
185 | | }; |
186 | | |
187 | | struct EntityRef |
188 | | { |
189 | | std::string schema_url{}; // == 1 |
190 | | std::string type{}; // == 2 |
191 | | std::vector<std::string> id_keys{}; // == 3 |
192 | | std::vector<std::string> description_keys{}; // == 4 |
193 | | |
194 | | void encode(protozero::pbf_writer& writer) const; |
195 | | static EntityRef decode(protozero::pbf_reader& reader); |
196 | | }; |
197 | | |
198 | | struct KeyValue |
199 | | { |
200 | | std::string key{}; // = 1 |
201 | | AnyValue value{}; // = 2 |
202 | | void encode(protozero::pbf_writer& writer) const; |
203 | | static KeyValue decode(protozero::pbf_reader& reader); |
204 | | |
205 | | bool operator==(const KeyValue& rhs) const |
206 | 0 | { |
207 | 0 | return key == rhs.key && value == rhs.value; |
208 | 0 | } |
209 | | }; |
210 | | |
211 | | struct Resource |
212 | | { |
213 | | std::vector<KeyValue> attributes{}; // = 1 |
214 | | uint32_t dropped_attributes_count{0}; // = 2; |
215 | | std::vector<EntityRef> entity_refs{}; // = 3 |
216 | | |
217 | | void encode(protozero::pbf_writer& writer) const; |
218 | | static Resource decode(protozero::pbf_reader& reader); |
219 | | }; |
220 | | |
221 | | struct InstrumentationScope |
222 | | { |
223 | | std::string name{}; // = 1 |
224 | | std::string version{}; // = 2 |
225 | | std::vector<KeyValue> attributes{}; // = 3 |
226 | | uint32_t dropped_attributes_count{0}; // = 4 |
227 | | |
228 | | void encode(protozero::pbf_writer& writer) const; |
229 | | static InstrumentationScope decode(protozero::pbf_reader& reader); |
230 | | }; |
231 | | |
232 | | struct TraceID : public std::array<uint8_t, 16> |
233 | | { |
234 | | [[nodiscard]] std::string toLogString() const; |
235 | | friend std::ostream& operator<<(std::ostream& ostrm, const TraceID& val) |
236 | 0 | { |
237 | 0 | return ostrm << val.toLogString(); |
238 | 0 | } |
239 | | |
240 | | static TraceID getRandomTraceID() |
241 | 0 | { |
242 | 0 | TraceID ret; |
243 | 0 | dns_random(ret.data(), ret.size()); |
244 | 0 | return ret; |
245 | 0 | } |
246 | | |
247 | | void makeRandom() |
248 | 0 | { |
249 | 0 | dns_random(this->data(), this->size()); |
250 | 0 | } |
251 | | |
252 | | void clear() |
253 | 0 | { |
254 | 0 | this->fill(0); |
255 | 0 | } |
256 | | }; |
257 | | constexpr TraceID s_emptyTraceID = {}; |
258 | | |
259 | | struct SpanID : public std::array<uint8_t, 8> |
260 | | { |
261 | | [[nodiscard]] std::string toLogString() const; |
262 | | friend std::ostream& operator<<(std::ostream& ostrm, const SpanID& val) |
263 | 0 | { |
264 | 0 | return ostrm << val.toLogString(); |
265 | 0 | } |
266 | | |
267 | | static SpanID getRandomSpanID() |
268 | 0 | { |
269 | 0 | SpanID ret; |
270 | 0 | dns_random(ret.data(), ret.size()); |
271 | 0 | return ret; |
272 | 0 | } |
273 | | |
274 | | void makeRandom() |
275 | 0 | { |
276 | 0 | dns_random(this->data(), this->size()); |
277 | 0 | } |
278 | | |
279 | | void clear() |
280 | 0 | { |
281 | 0 | this->fill(0); |
282 | 0 | } |
283 | | }; |
284 | | constexpr SpanID s_emptySpanID = {}; |
285 | | |
286 | | inline void fill(TraceID& trace, const std::string& data) |
287 | 0 | { |
288 | 0 | if (data.size() != trace.size()) { |
289 | 0 | throw std::runtime_error("TraceID size mismatch"); |
290 | 0 | } |
291 | 0 | std::copy(data.begin(), data.end(), trace.begin()); |
292 | 0 | } |
293 | | |
294 | | inline void fill(SpanID& span, const std::string& data) |
295 | 0 | { |
296 | 0 | if (data.size() != span.size()) { |
297 | 0 | throw std::runtime_error("SpanID size mismatch"); |
298 | 0 | } |
299 | 0 | std::copy(data.begin(), data.end(), span.begin()); |
300 | 0 | } |
301 | | |
302 | | inline void fill(TraceID& trace, const char* data, size_t size) |
303 | 0 | { |
304 | 0 | fill(trace, std::string(data, size)); |
305 | 0 | } |
306 | | |
307 | | inline void fill(SpanID& span, const char* data, size_t size) |
308 | 0 | { |
309 | 0 | fill(span, std::string(data, size)); |
310 | 0 | } |
311 | | |
312 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, const TraceID& value) |
313 | 0 | { |
314 | 0 | writer.add_bytes(field, reinterpret_cast<const char*>(value.data()), value.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) it's the API |
315 | 0 | } |
316 | | |
317 | | inline TraceID decodeTraceID(protozero::pbf_reader& reader) |
318 | 0 | { |
319 | 0 | TraceID bytes; |
320 | 0 | const auto data = reader.get_view(); |
321 | 0 | const auto len = std::min(bytes.size(), data.size()); |
322 | 0 | std::copy(data.data(), data.data() + len, bytes.begin()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
323 | 0 | return bytes; |
324 | 0 | } |
325 | | |
326 | | inline void encode(protozero::pbf_writer& writer, uint8_t field, const SpanID& value) |
327 | 0 | { |
328 | 0 | writer.add_bytes(field, reinterpret_cast<const char*>(value.data()), value.size()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) it's the API |
329 | 0 | } |
330 | | |
331 | | inline SpanID decodeSpanID(protozero::pbf_reader& reader) |
332 | 0 | { |
333 | 0 | SpanID bytes; |
334 | 0 | const auto data = reader.get_view(); |
335 | 0 | const auto len = std::min(bytes.size(), data.size()); |
336 | 0 | std::copy(data.data(), data.data() + len, bytes.begin()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
337 | 0 | return bytes; |
338 | 0 | } |
339 | | |
340 | | // The Status type defines a logical error model that is suitable for different |
341 | | // programming environments, including REST APIs and RPC APIs. |
342 | | struct Status |
343 | | { |
344 | | // A developer-facing human readable error message. |
345 | | std::string message{}; // = 2; |
346 | | |
347 | | // For the semantics of status codes see |
348 | | // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status |
349 | | enum class StatusCode : uint8_t |
350 | | { |
351 | | // The default status. |
352 | | STATUS_CODE_UNSET = 0, |
353 | | // The Span has been validated by an Application developer or Operator to |
354 | | // have completed successfully. |
355 | | STATUS_CODE_OK = 1, |
356 | | // The Span contains an error. |
357 | | STATUS_CODE_ERROR = 2, |
358 | | }; |
359 | | |
360 | | // The status code. |
361 | | StatusCode code{StatusCode::STATUS_CODE_UNSET}; // = 3; |
362 | | |
363 | | void clear() |
364 | 0 | { |
365 | 0 | message.clear(); |
366 | 0 | code = StatusCode::STATUS_CODE_UNSET; |
367 | 0 | } |
368 | | void encode(protozero::pbf_writer& writer) const; |
369 | | static Status decode(protozero::pbf_reader& reader); |
370 | | }; |
371 | | |
372 | | inline uint64_t timestamp() |
373 | 0 | { |
374 | 0 | timespec now{}; |
375 | 0 | clock_gettime(CLOCK_REALTIME, &now); |
376 | 0 | return (1000000000ULL * now.tv_sec) + now.tv_nsec; |
377 | 0 | } |
378 | | |
379 | | // This struct is used to store the info of the initial span. As it is passed around resolving |
380 | | // queries, it needs to be as small as possible, hence no full Span. |
381 | | struct InitialSpanInfo |
382 | | { |
383 | | TraceID trace_id{}; |
384 | | SpanID span_id{}; |
385 | | SpanID parent_span_id{}; |
386 | | uint64_t start_time_unix_nano{0}; |
387 | | |
388 | | void clear() |
389 | 0 | { |
390 | 0 | trace_id.clear(); |
391 | 0 | span_id.clear(); |
392 | 0 | parent_span_id.clear(); |
393 | 0 | start_time_unix_nano = 0; |
394 | 0 | } |
395 | | }; |
396 | | |
397 | | struct Span |
398 | | { |
399 | | // A unique identifier for a trace. All spans from the same trace share |
400 | | // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR |
401 | | // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON |
402 | | // is zero-length and thus is also invalid). |
403 | | // |
404 | | // This field is required. |
405 | | TraceID trace_id{}; // = 1 |
406 | | // A unique identifier for a span within a trace, assigned when the span |
407 | | // is created. The ID is an 8-byte array. An ID with all zeroes OR of length |
408 | | // other than 8 bytes is considered invalid (empty string in OTLP/JSON |
409 | | // is zero-length and thus is also invalid). |
410 | | // |
411 | | // This field is required. |
412 | | SpanID span_id{}; // = 2 |
413 | | // trace_state conveys information about request position in multiple distributed tracing graphs. |
414 | | // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header |
415 | | // See also https://github.com/w3c/distributed-tracing for more details about this field. |
416 | | std::string trace_state{}; // = 3 |
417 | | // The `span_id` of this span's parent span. If this is a root span, then this |
418 | | // field must be empty. The ID is an 8-byte array. |
419 | | SpanID parent_span_id{}; // = 4 |
420 | | // A description of the span's operation. |
421 | | // |
422 | | // For example, the name can be a qualified method name or a file name |
423 | | // and a line number where the operation is called. A best practice is to use |
424 | | // the same display name at the same call point in an application. |
425 | | // This makes it easier to correlate spans in different traces. |
426 | | // |
427 | | // This field is semantically required to be set to non-empty string. |
428 | | // Empty value is equivalent to an unknown span name. |
429 | | // |
430 | | // This field is required. |
431 | | std::string name{}; // = 5 |
432 | | |
433 | | // SpanKind is the type of span. Can be used to specify additional relationships between spans |
434 | | // in addition to a parent/child relationship. |
435 | | enum class SpanKind : uint8_t |
436 | | { |
437 | | // Unspecified. Do NOT use as default. |
438 | | // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. |
439 | | SPAN_KIND_UNSPECIFIED = 0, |
440 | | // Indicates that the span represents an internal operation within an application, |
441 | | // as opposed to an operation happening at the boundaries. Default value. |
442 | | SPAN_KIND_INTERNAL = 1, |
443 | | // Indicates that the span covers server-side handling of an RPC or other |
444 | | // remote network request. |
445 | | SPAN_KIND_SERVER = 2, |
446 | | // Indicates that the span describes a request to some remote service. |
447 | | SPAN_KIND_CLIENT = 3, |
448 | | // Indicates that the span describes a producer sending a message to a broker. |
449 | | // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship |
450 | | // between producer and consumer spans. A PRODUCER span ends when the message was accepted |
451 | | // by the broker while the logical processing of the message might span a much longer time. |
452 | | SPAN_KIND_PRODUCER = 4, |
453 | | // Indicates that the span describes consumer receiving a message from a broker. |
454 | | // Like the PRODUCER kind, there is often no direct critical path latency relationship |
455 | | // between producer and consumer spans. |
456 | | SPAN_KINCONSUMER = 5, |
457 | | }; |
458 | | // Distinguishes between spans generated in a particular context. For example, |
459 | | // two spans with the same name may be distinguished using `CLIENT` (caller) |
460 | | // and `SERVER` (callee) to identify queueing latency associated with the span. |
461 | | SpanKind kind{Span::SpanKind::SPAN_KIND_UNSPECIFIED}; // = 6 |
462 | | // start_time_unix_nano is the start time of the span. On the client side, this is the time |
463 | | // kept by the local machine where the span execution starts. On the server side, this |
464 | | // is the time when the server's application handler starts running. |
465 | | // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. |
466 | | // |
467 | | // This field is semantically required and it is expected that end_time >= start_time. |
468 | | uint64_t start_time_unix_nano{0}; // = 7 |
469 | | // end_time_unix_nano is the end time of the span. On the client side, this is the time |
470 | | // kept by the local machine where the span execution ends. On the server side, this |
471 | | // is the time when the server application handler stops running. |
472 | | // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. |
473 | | // |
474 | | // This field is semantically required and it is expected that end_time >= start_time. |
475 | | uint64_t end_time_unix_nano{0}; // = 8 |
476 | | // attributes is a collection of key/value pairs. Note, global attributes |
477 | | // like server name can be set using the resource API. Examples of attributes: |
478 | | // |
479 | | // "/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" |
480 | | // "/http/server_latency": 300 |
481 | | // "example.com/myattribute": true |
482 | | // "example.com/score": 10.239 |
483 | | // |
484 | | // The OpenTelemetry API specification further restricts the allowed value types: |
485 | | // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute |
486 | | // Attribute keys MUST be unique (it is not allowed to have more than one |
487 | | // attribute with the same key). |
488 | | std::vector<KeyValue> attributes{}; // = 9 |
489 | | // dropped_attributes_count is the number of attributes that were discarded. Attributes |
490 | | // can be discarded because their keys are too long or because there are too many |
491 | | // attributes. If this value is 0, then no attributes were dropped. |
492 | | uint32_t dropped_attributes_count{0}; // = 10 |
493 | | |
494 | | // Event is a time-stamped annotation of the span, consisting of user-supplied |
495 | | // text description and key-value pairs.x |
496 | | struct Event |
497 | | { |
498 | | // time_unix_nano is the time the event occurred. |
499 | | uint64_t time_unix_nano; // = 1 |
500 | | // name of the event. |
501 | | // This field is semantically required to be set to non-empty string. |
502 | | std::string name; // = 2 |
503 | | // attributes is a collection of attribute key/value pairs on the event. |
504 | | // Attribute keys MUST be unique (it is not allowed to have more than one |
505 | | // attribute with the same key). |
506 | | std::vector<KeyValue> attributes; // = 3 |
507 | | // dropped_attributes_count is the number of dropped attributes. If the value is 0, |
508 | | // then no attributes were dropped. |
509 | | uint32_t dropped_attributes_count{0}; // = 4 |
510 | | |
511 | | void encode(protozero::pbf_writer& writer) const; |
512 | | static Event decode(protozero::pbf_reader& reader); |
513 | | }; |
514 | | // events is a collection of Event items. |
515 | | std::vector<Event> events{}; // = 11 |
516 | | // dropped_events_count is the number of dropped events. If the value is 0, then no |
517 | | // events were dropped. |
518 | | uint32_t dropped_events_count{0}; // = 12 |
519 | | |
520 | | // A pointer from the current span to another span in the same trace or in a |
521 | | // different trace. For example, this can be used in batching operations, |
522 | | // where a single batch handler processes multiple requests from different |
523 | | // traces or when the handler receives a request from a different project. |
524 | | struct Link |
525 | | { |
526 | | // A unique identifier of a trace that this linked span is part of. The ID is a |
527 | | // 16-byte array. |
528 | | TraceID trace_id; // = 1 |
529 | | // A unique identifier for the linked span. The ID is an 8-byte array. |
530 | | SpanID span_id; // = 2 |
531 | | // The trace_state associated with the link. |
532 | | std::string trace_state; // = 3 |
533 | | // attributes is a collection of attribute key/value pairs on the link. |
534 | | // Attribute keys MUST be unique (it is not allowed to have more than one |
535 | | // attribute with the same key). |
536 | | std::vector<KeyValue> attributes; // = 4 |
537 | | // dropped_attributes_count is the number of dropped attributes. If the value is 0, |
538 | | // then no attributes were dropped. |
539 | | uint32_t dropped_attributes_count{0}; // = 5 |
540 | | // Flags, a bit field. |
541 | | // |
542 | | // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace |
543 | | // Context specification. To read the 8-bit W3C trace flag, use |
544 | | // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. |
545 | | // |
546 | | // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. |
547 | | // |
548 | | // Bits 8 and 9 represent the 3 states of whether the link is remote. |
549 | | // The states are (unknown, is not remote, is remote). |
550 | | // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. |
551 | | // To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. |
552 | | // |
553 | | // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. |
554 | | // When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. |
555 | | // |
556 | | // [Optional]. |
557 | | uint32_t flags{0}; // = 6 |
558 | | |
559 | | void encode(protozero::pbf_writer& writer) const; |
560 | | static Link decode(protozero::pbf_reader& reader); |
561 | | }; |
562 | | std::vector<Link> links{}; // = 13 |
563 | | uint32_t dropped_links_count{0}; // = 14 |
564 | | Status status{}; // = 15 |
565 | | |
566 | | // Flags, a bit field. |
567 | | // |
568 | | // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace |
569 | | // Context specification. To read the 8-bit W3C trace flag, use |
570 | | // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. |
571 | | // |
572 | | // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. |
573 | | // |
574 | | // Bits 8 and 9 represent the 3 states of whether a span's parent |
575 | | // is remote. The states are (unknown, is not remote, is remote). |
576 | | // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. |
577 | | // To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. |
578 | | // |
579 | | // When creating span messages, if the message is logically forwarded from another source |
580 | | // with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD |
581 | | // be copied as-is. If creating from a source that does not have an equivalent flags field |
582 | | // (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST |
583 | | // be set to zero. |
584 | | // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. |
585 | | // |
586 | | // [Optional]. |
587 | | uint32_t flags{0}; // = 16; |
588 | | |
589 | | void close() |
590 | 0 | { |
591 | 0 | end_time_unix_nano = timestamp(); |
592 | 0 | } |
593 | | |
594 | | void clear() |
595 | 0 | { |
596 | 0 | trace_id.clear(); // 1 |
597 | 0 | span_id.clear(); // 2 |
598 | 0 | trace_state.clear(); // 3 |
599 | 0 | parent_span_id.clear(); // 4 |
600 | 0 | name.clear(); // 5 |
601 | 0 | kind = SpanKind::SPAN_KIND_UNSPECIFIED; // 6 |
602 | 0 | start_time_unix_nano = 0; // 7 |
603 | 0 | end_time_unix_nano = 0; // 8 |
604 | 0 | attributes.clear(); // 9 |
605 | 0 | dropped_attributes_count = 0; // 10 |
606 | 0 | events.clear(); // 11 |
607 | 0 | dropped_events_count = 0; // 12 |
608 | 0 | links.clear(); // 13 |
609 | 0 | dropped_links_count = 0; //14 |
610 | 0 | status.clear(); // 15 |
611 | 0 | flags = 0; // 16 |
612 | 0 | } |
613 | | void encode(protozero::pbf_writer& writer) const; |
614 | | static Span decode(protozero::pbf_reader& reader); |
615 | | }; |
616 | | |
617 | | // SpanFlags represents constants used to interpret the |
618 | | // Span.flags field, which is protobuf 'fixed32' type and is to |
619 | | // be used as bit-fields. Each non-zero value defined in this enum is |
620 | | // a bit-mask. To extract the bit-field, for example, use an |
621 | | // expression like: |
622 | | // |
623 | | // (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) |
624 | | // |
625 | | // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. |
626 | | // |
627 | | // Note that Span flags were introduced in version 1.1 of the |
628 | | // OpenTelemetry protocol. Older Span producers do not set this |
629 | | // field, consequently consumers should not rely on the absence of a |
630 | | // particular flag bit to indicate the presence of a particular feature. |
631 | | enum class SpanFlags : uint16_t |
632 | | { |
633 | | // The zero value for the enum. Should not be used for comparisons. |
634 | | // Instead use bitwise "and" with the appropriate mask as shown above. |
635 | | SPAN_FLAGS_DO_NOT_USE = 0, |
636 | | // Bits 0-7 are used for trace flags. |
637 | | SPAN_FLAGS_TRACE_FLAGS_MASK = 0x000000FF, |
638 | | // Bits 8 and 9 are used to indicate that the parent span or link span is remote. |
639 | | // Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. |
640 | | // Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. |
641 | | SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 0x00000100, |
642 | | SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 0x00000200, |
643 | | // Bits 10-31 are reserved for future use. |
644 | | }; |
645 | | |
646 | | // A collection of Spans produced by an InstrumentationScope. |
647 | | struct ScopeSpans |
648 | | { |
649 | | // The instrumentation scope information for the spans in this message. |
650 | | // Semantically when InstrumentationScope isn't set, it is equivalent with |
651 | | // an empty instrumentation scope name (unknown). |
652 | | InstrumentationScope scope{}; // = 1 |
653 | | // A list of Spans that originate from an instrumentation scope. |
654 | | std::vector<Span> spans{}; // = 2 |
655 | | // The Schema URL, if known. This is the identifier of the Schema that the span data |
656 | | // is recorded in. Notably, the last part of the URL path is the version number of the |
657 | | // schema: http[s]://server[:port]/path/<version>. To learn more about Schema URL see |
658 | | // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url |
659 | | // This schema_url applies to all spans and span events in the "spans" field. |
660 | | std::string schema_url{}; // = 3 |
661 | | |
662 | | void close() |
663 | 0 | { |
664 | 0 | for (auto& element : spans) { |
665 | 0 | element.close(); |
666 | 0 | } |
667 | 0 | } |
668 | | void encode(protozero::pbf_writer& writer) const; |
669 | | static ScopeSpans decode(protozero::pbf_reader& reader); |
670 | | }; |
671 | | |
672 | | // A collection of ScopeSpans from a Resource. |
673 | | struct ResourceSpans |
674 | | { |
675 | | // The resource for the spans in this message. |
676 | | // If this field is not set then no resource info is known. |
677 | | Resource resource; // = 1 |
678 | | // A list of ScopeSpans that originate from a resource. |
679 | | std::vector<ScopeSpans> scope_spans; // = 2 |
680 | | // The Schema URL, if known. This is the identifier of the Schema that the resource data |
681 | | // is recorded in. Notably, the last part of the URL path is the version number of the |
682 | | // schema: http[s]://server[:port]/path/<version>. To learn more about Schema URL see |
683 | | // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url |
684 | | // This schema_url applies to the data in the "resource" field. It does not apply |
685 | | // to the data in the "scope_spans" field which have their own schema_url field. |
686 | | std::string schema_url{}; // = 3 |
687 | | |
688 | | void close() |
689 | 0 | { |
690 | 0 | for (auto& element : scope_spans) { |
691 | 0 | element.close(); |
692 | 0 | } |
693 | 0 | } |
694 | | void encode(protozero::pbf_writer& writer) const; |
695 | | static ResourceSpans decode(protozero::pbf_reader& reader); |
696 | | }; |
697 | | |
698 | | // TracesData represents the traces data that can be stored in a persistent storage, |
699 | | // OR can be embedded by other protocols that transfer OTLP traces data but do |
700 | | // not implement the OTLP protocol. |
701 | | // |
702 | | // The main difference between this message and collector protocol is that |
703 | | // in this message there will not be any "control" or "metadata" specific to |
704 | | // OTLP protocol. |
705 | | // |
706 | | // When new fields are added into this message, the OTLP request MUST be updated |
707 | | // as well. |
708 | | struct TracesData |
709 | | { |
710 | | // An array of ResourceSpans. |
711 | | // For data coming from a single resource this array will typically contain |
712 | | // one element. Intermediary nodes that receive data from multiple origins |
713 | | // typically batch the data before forwarding further and in that case this |
714 | | // array will contain multiple elements. |
715 | | std::vector<ResourceSpans> resource_spans; // = 1 |
716 | | |
717 | | void close() |
718 | 0 | { |
719 | 0 | for (auto& element : resource_spans) { |
720 | 0 | element.close(); |
721 | 0 | } |
722 | 0 | } |
723 | | void encode(protozero::pbf_writer& writer) const; |
724 | | static TracesData decode(protozero::pbf_reader& reader); |
725 | | |
726 | | [[nodiscard]] std::string encode() const |
727 | 0 | { |
728 | 0 | std::string data; |
729 | 0 | protozero::pbf_writer writer{data}; |
730 | 0 | encode(writer); |
731 | 0 | return data; |
732 | 0 | } |
733 | | |
734 | | static TracesData boilerPlate(std::string&& service, std::vector<Span>&& spans, const std::vector<KeyValue>& attributes, std::string& serverID) |
735 | 0 | { |
736 | 0 | auto& spanAttrs = spans.at(0).attributes; |
737 | 0 | spanAttrs.insert(spanAttrs.end(), attributes.begin(), attributes.end()); |
738 | 0 | auto host = getHostname(); |
739 | 0 | std::string hostname = host.value_or("unset"); |
740 | 0 | InstrumentationScope scope{ |
741 | 0 | .name = "rec", .version = VERSION, .attributes = {{"hostname", {hostname}}, {"server.id", {serverID}}}}; |
742 | 0 | return TracesData{ |
743 | 0 | .resource_spans = {pdns::trace::ResourceSpans{.resource = {.attributes = {{"service.name", {{std::move(service)}}}}}, .scope_spans = {{.scope = std::move(scope), .spans = std::move(spans)}}}}}; |
744 | 0 | } |
745 | | }; |
746 | | |
747 | | inline ArrayValue ArrayValue::decode(protozero::pbf_reader& reader) |
748 | 0 | { |
749 | 0 | return pdns::trace::decode<ArrayValue, AnyValue>(reader); |
750 | 0 | } |
751 | | |
752 | | inline KeyValueList KeyValueList::decode(protozero::pbf_reader& reader) |
753 | 0 | { |
754 | 0 | return pdns::trace::decode<KeyValueList, KeyValue>(reader); |
755 | 0 | } |
756 | | |
757 | | struct EDNSOTTraceRecord |
758 | | { |
759 | | // 1 byte version, 1 byte reserved/alignment, 16 bytes traceid, optional 8 bytes spanid |
760 | | static constexpr size_t fullSize = 1 + 1 + 16 + 8; |
761 | | static constexpr size_t sizeNoSpanID = 1 + 1 + 16; |
762 | | static constexpr size_t traceIDOffset = 1 + 1; |
763 | | static constexpr size_t spanIDOffset = 1 + 1 + 16; |
764 | | |
765 | | EDNSOTTraceRecord(uint8_t* arg) : |
766 | 0 | data(arg) {} |
767 | | // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
768 | | void setVersion(uint8_t version) |
769 | 0 | { |
770 | 0 | data[0] = version; |
771 | 0 | } |
772 | | void setTraceID(const TraceID& traceid) |
773 | 0 | { |
774 | 0 | std::copy(traceid.begin(), traceid.end(), &data[traceIDOffset]); |
775 | 0 | } |
776 | | void setSpanID(const SpanID& spanid) |
777 | 0 | { |
778 | 0 | std::copy(spanid.begin(), spanid.end(), &data[spanIDOffset]); |
779 | 0 | } |
780 | | // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
781 | | private: |
782 | | uint8_t* data; |
783 | | }; |
784 | | |
785 | | struct EDNSOTTraceRecordView |
786 | | { |
787 | | EDNSOTTraceRecordView(const uint8_t* arg, size_t argsize) : |
788 | 0 | data(arg), size(argsize) {} |
789 | | |
790 | | // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
791 | | [[nodiscard]] bool getVersion(uint8_t& version) const |
792 | 0 | { |
793 | 0 | if (size > 0) { |
794 | 0 | version = data[0]; |
795 | 0 | return true; |
796 | 0 | } |
797 | 0 | return false; |
798 | 0 | } |
799 | | [[nodiscard]] bool getTraceID(TraceID& traceid) const |
800 | 0 | { |
801 | 0 | if (size >= pdns::trace::EDNSOTTraceRecord::sizeNoSpanID) { |
802 | 0 | std::copy(&data[EDNSOTTraceRecord::traceIDOffset], &data[EDNSOTTraceRecord::traceIDOffset + traceid.size()], traceid.begin()); |
803 | 0 | return true; |
804 | 0 | } |
805 | 0 | return false; |
806 | 0 | } |
807 | | [[nodiscard]] bool getSpanID(SpanID& spanid) const |
808 | 0 | { |
809 | 0 | if (size == pdns::trace::EDNSOTTraceRecord::fullSize) { |
810 | 0 | std::copy(&data[EDNSOTTraceRecord::spanIDOffset], &data[EDNSOTTraceRecord::spanIDOffset + spanid.size()], spanid.begin()); |
811 | 0 | return true; |
812 | 0 | } |
813 | 0 | return false; |
814 | 0 | } |
815 | | // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) |
816 | | private: |
817 | | const uint8_t* const data; |
818 | | const size_t size; |
819 | | }; |
820 | | |
821 | | void extractOTraceIDs(const EDNSOptionViewMap& map, pdns::trace::InitialSpanInfo& span); |
822 | | |
823 | | } // namespace pdns::trace |