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