/src/pdns/pdns/dnsdistdist/dnsdist-opentelemetry.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 | | #pragma once |
23 | | |
24 | | #include <memory> |
25 | | #include <string> |
26 | | #include <vector> |
27 | | #include <optional> |
28 | | |
29 | | #include "ednsoptions.hh" |
30 | | #include "remote_logger.hh" |
31 | | |
32 | | #ifndef DISABLE_PROTOBUF |
33 | | #include "protozero-trace.hh" |
34 | | using TraceID = pdns::trace::TraceID; |
35 | | using SpanID = pdns::trace::SpanID; |
36 | | using AnyValue = pdns::trace::AnyValue; |
37 | | using TracesData = pdns::trace::TracesData; |
38 | | using SpanKind = pdns::trace::Span::SpanKind; |
39 | | #else |
40 | | // Define the minimal things needed |
41 | | #include <variant> |
42 | | using TraceID = int; |
43 | | using SpanID = int; |
44 | | using AnyValue = std::variant<std::string, int>; |
45 | | using TracesData = int; |
46 | | using SpanKind = int; |
47 | | #endif |
48 | | |
49 | | #include "lock.hh" |
50 | | |
51 | | /* |
52 | | * This namespace contains all the bits and pieces required to do OpenTelemetry |
53 | | * traces in dnsdist. It is contained in this header and cc-file to ensure the rest |
54 | | * of the code is not littered with #ifdefs for DISABLE_PROTOBUF. All functions and |
55 | | * other public members can be safely called/manipulated in a non-protobuf build of |
56 | | * dnsdist. |
57 | | * |
58 | | * The idea is inspired by the rec-eventtrace.{cc,hh} files. |
59 | | * |
60 | | * Although the namespace contains dnsdist, it might be general enough to be |
61 | | * reused (after renaming the namespace) by auth and recursor |
62 | | */ |
63 | | namespace pdns::trace::dnsdist |
64 | | { |
65 | | |
66 | | /** |
67 | | * @class Tracer |
68 | | * @brief This class holds a single trace instance |
69 | | * |
70 | | */ |
71 | | class Tracer : public std::enable_shared_from_this<Tracer> |
72 | | { |
73 | | public: |
74 | 0 | ~Tracer() = default; |
75 | | Tracer(const Tracer&) = delete; |
76 | | Tracer& operator=(const Tracer) = delete; |
77 | | Tracer& operator=(Tracer&&) = delete; |
78 | | Tracer(Tracer&&) = delete; |
79 | | |
80 | | /** |
81 | | * @brief get a new Tracer |
82 | | */ |
83 | | static std::shared_ptr<Tracer> getTracer() |
84 | 0 | { |
85 | 0 | return std::shared_ptr<Tracer>(new Tracer); |
86 | 0 | } |
87 | | |
88 | | /** |
89 | | * @brief Set the TraceID |
90 | | * |
91 | | * @param traceID |
92 | | */ |
93 | | void setTraceID(const TraceID& traceID); |
94 | | |
95 | | /** |
96 | | * @brief Set the SpanID for the root and re-parent |
97 | | * |
98 | | * @param spanID |
99 | | */ |
100 | | void setRootSpanID(const SpanID& spanID); |
101 | | |
102 | | /** |
103 | | * @brief Set the name for the Scope Span |
104 | | * |
105 | | * @param name |
106 | | */ |
107 | | void setScopeSpanName(const std::string& name); |
108 | | |
109 | | /** |
110 | | * @brief Add an attribute to the Trace |
111 | | * |
112 | | * @param key |
113 | | * @param value |
114 | | * @return true on success, false when attribute was not added |
115 | | */ |
116 | | bool setTraceAttribute(const std::string& key, const AnyValue& value); |
117 | | |
118 | | /** |
119 | | * @brief Set an attribute on the root span |
120 | | * |
121 | | * @param key |
122 | | * @param value |
123 | | */ |
124 | | void setRootSpanAttribute(const std::string& key, const AnyValue& value); |
125 | | |
126 | | /** |
127 | | * @brief Set an attribute on a Span |
128 | | * |
129 | | * This does not work when the Tracer is not active |
130 | | * |
131 | | * @param spanID The SpanID of the Span to add the attribute to |
132 | | * @param key |
133 | | * @param value |
134 | | */ |
135 | | void setSpanAttribute(const SpanID& spanID, const std::string& key, const AnyValue& value); |
136 | | |
137 | | /** |
138 | | * @brief Sets the Kind of a Span |
139 | | * |
140 | | * This does not work when the Tracer is not active |
141 | | * |
142 | | * @param spanID The SpanID of the Span to add the attribute to |
143 | | * @param spanking The Kind to set the Span to |
144 | | */ |
145 | | void setSpanKind(const SpanID& spanID, const SpanKind spankind); |
146 | | |
147 | | /** |
148 | | * @brief Sets the stop timestamp for a span |
149 | | * |
150 | | * When a Span is already closed, the timestamp is not updated |
151 | | * |
152 | | * @param spanID The ID of the Span to set the end time for |
153 | | */ |
154 | | void closeSpan(const SpanID& spanID); |
155 | | |
156 | | /** |
157 | | * @brief Get the top-most SpanID |
158 | | * |
159 | | * @return The SpanID of the root Span |
160 | | */ |
161 | | [[nodiscard]] SpanID getRootSpanID(); |
162 | | |
163 | | /** |
164 | | * @brief Get the last SpanID generated |
165 | | * |
166 | | * @return The last generated SpanID, or empty SpanID when none exist |
167 | | */ |
168 | | [[nodiscard]] SpanID getLastSpanID(); |
169 | | |
170 | | /** |
171 | | * @brief Get the SpanID for the most recently added span with a name |
172 | | * |
173 | | * @param name The name of the Span |
174 | | * @return The SpanID, or empty SpanID when none are found |
175 | | */ |
176 | | [[nodiscard]] SpanID getLastSpanIDForName(const std::string& name); |
177 | | |
178 | | /** |
179 | | * @brief Retrieve the TraceID for this Tracer |
180 | | */ |
181 | | [[nodiscard]] TraceID getTraceID(); |
182 | | |
183 | | /** |
184 | | * @brief Generate the TracesData from all data in this Tracer |
185 | | * |
186 | | * @return pdns::trace::TracesData |
187 | | */ |
188 | | [[nodiscard]] TracesData getTracesData(); |
189 | | |
190 | | /** |
191 | | * @brief Get the TracesData as protobuf encoded OpenTelemetry data |
192 | | */ |
193 | | [[nodiscard]] std::string getOTProtobuf(); |
194 | | |
195 | | /** |
196 | | * @class Closer |
197 | | * @brief Automatically closes a Span when it goes out of scope |
198 | | * |
199 | | * This is a helper that _somewhat_ implements Go's `defer` in C++ semantics |
200 | | * Basically, it stores a pointer to the Tracer and a SpanID. |
201 | | * When the object goes out of scope, the closeSpan function is called |
202 | | */ |
203 | | class Closer |
204 | | { |
205 | | public: |
206 | | /** |
207 | | * @brief An empty Closer, not really useful |
208 | | */ |
209 | | Closer() = default; |
210 | | |
211 | | #ifndef DISABLE_PROTOBUF |
212 | | /** |
213 | | * @brief Create a Closer |
214 | | * |
215 | | * There should be no need to call this directly. Use one of these functions to get one: |
216 | | * |
217 | | * Tracer::getCloser |
218 | | * Tracer::openSpan |
219 | | * |
220 | | * @param tracer A pointer to the Tracer where we want to close a Span |
221 | | * @param spanid The SpanID to close in the Tracer |
222 | | */ |
223 | | Closer(std::shared_ptr<Tracer> tracer, const SpanID& spanid) : |
224 | 0 | d_tracer(std::move(tracer)), d_spanID(spanid) {}; |
225 | | |
226 | | #endif |
227 | | |
228 | | /** |
229 | | * @brief Closes the Span in the Tracer |
230 | | */ |
231 | | ~Closer() |
232 | 0 | { |
233 | 0 | #ifndef DISABLE_PROTOBUF |
234 | 0 | if (d_tracer != nullptr) { |
235 | 0 | d_tracer->closeSpan(d_spanID); |
236 | 0 | } |
237 | 0 | #endif |
238 | 0 | } |
239 | | Closer(const Closer&) = delete; |
240 | | Closer& operator=(const Closer&) = delete; |
241 | | Closer& operator=([[maybe_unused]] Closer&& rhs) noexcept |
242 | 0 | { |
243 | 0 | #ifndef DISABLE_PROTOBUF |
244 | 0 | this->d_tracer = std::move(rhs.d_tracer); |
245 | 0 | this->d_spanID = rhs.d_spanID; |
246 | | /* we wouldn't want to close it twice */ |
247 | 0 | rhs.d_tracer.reset(); |
248 | 0 | rhs.d_spanID.clear(); |
249 | 0 | #endif |
250 | 0 | return *this; |
251 | 0 | } |
252 | | Closer([[maybe_unused]] Closer&& rhs) noexcept |
253 | 0 | { |
254 | 0 | #ifndef DISABLE_PROTOBUF |
255 | 0 | this->d_tracer = std::move(rhs.d_tracer); |
256 | 0 | this->d_spanID = rhs.d_spanID; |
257 | | /* we wouldn't want to close it twice */ |
258 | 0 | rhs.d_tracer.reset(); |
259 | 0 | rhs.d_spanID.clear(); |
260 | 0 | #endif |
261 | 0 | } |
262 | | |
263 | | /** |
264 | | * @brief Get the SpanID |
265 | | * |
266 | | * @return |
267 | | */ |
268 | | [[nodiscard]] SpanID getSpanID() const; |
269 | | |
270 | | /** |
271 | | * @brief Set an attribute on the Span |
272 | | * |
273 | | * @param key |
274 | | * @param value |
275 | | * @return |
276 | | */ |
277 | | void setAttribute(const std::string& key, const AnyValue& value); |
278 | | |
279 | | /** |
280 | | * @brief Set the Kind of Span |
281 | | * |
282 | | * @param spankind |
283 | | */ |
284 | | void setKind(const SpanKind); |
285 | | |
286 | | private: |
287 | | #ifndef DISABLE_PROTOBUF |
288 | | std::shared_ptr<Tracer> d_tracer{nullptr}; |
289 | | SpanID d_spanID{}; |
290 | | #endif |
291 | | }; |
292 | | |
293 | | /** |
294 | | * @brief Get a Closer for spanid in this Tracer |
295 | | * |
296 | | * @param spanid The SpanID that will close when the Closer is destructed |
297 | | * @return Tracer::Closer |
298 | | */ |
299 | | Closer getCloser(const SpanID& spanid); |
300 | | |
301 | | /** |
302 | | * @brief Add a new Span |
303 | | * |
304 | | * @param name The name for this span |
305 | | * @return Tracer::Closer for the newly created Span |
306 | | */ |
307 | | Closer openSpan(const std::string& name); |
308 | | |
309 | | /** |
310 | | * @brief Add a new Span which is a child of another Span |
311 | | * |
312 | | * @param name The name for this span |
313 | | * @param parentSpanID The SpanID of the parent Trace |
314 | | * @return Tracer::Closer for the newly created Span |
315 | | */ |
316 | | Closer openSpan(const std::string& name, const SpanID& parentSpanID); |
317 | | |
318 | | private: |
319 | 0 | Tracer() = default; |
320 | | |
321 | | /** |
322 | | * @brief Create a new Span |
323 | | * |
324 | | * The Span's start time is set to the current time |
325 | | * |
326 | | * @param name The name for this span |
327 | | * @return The SpanID of the created Span |
328 | | */ |
329 | | SpanID addSpan(const std::string& name); |
330 | | |
331 | | /** |
332 | | * @brief Create a new Span with a parent |
333 | | * |
334 | | * The Span's start time is set to the current time |
335 | | * |
336 | | * @param name The name for this span |
337 | | * @param parentSpanID The SpanID of the parent Span (not verified) |
338 | | * @return The SpanID of the created Span |
339 | | */ |
340 | | SpanID addSpan(const std::string& name, const SpanID& parentSpanID); |
341 | | |
342 | | #ifndef DISABLE_PROTOBUF |
343 | | /** |
344 | | * @class miniSpan |
345 | | * @brief Used to store Span information |
346 | | */ |
347 | | struct miniSpan |
348 | | { |
349 | | std::string name; |
350 | | SpanID span_id; |
351 | | SpanID parent_span_id; |
352 | | SpanKind span_kind; |
353 | | uint64_t start_time_unix_nano; |
354 | | uint64_t end_time_unix_nano; |
355 | | std::vector<pdns::trace::KeyValue> attributes; |
356 | | }; |
357 | | |
358 | | struct Data |
359 | | { |
360 | | /** |
361 | | * @brief Stores all miniSpans. |
362 | | */ |
363 | | std::vector<miniSpan> d_spans; |
364 | | |
365 | | /** |
366 | | * @brief All attributes related to this Trace (added to the ScopeSpan) |
367 | | */ |
368 | | std::vector<pdns::trace::KeyValue> d_attributes; |
369 | | |
370 | | /** |
371 | | * @brief The TraceID for this Tracer. It is stable for the lifetime of the Tracer |
372 | | */ |
373 | | TraceID d_traceid{}; |
374 | | |
375 | | /** |
376 | | * @brief A stack of SpanID's that tracks the "stack" of SpanIDs |
377 | | */ |
378 | | std::vector<SpanID> d_spanIDStack; |
379 | | |
380 | | /** |
381 | | * Set when setRootSpanID is called, used to replace the |
382 | | * root span id (and the parent span ids) when the PB is generated |
383 | | */ |
384 | | struct |
385 | | { |
386 | | SpanID oldID; |
387 | | SpanID newID; |
388 | | } d_oldAndNewRootSpanID; |
389 | | |
390 | | std::string scope_span_name; |
391 | | }; |
392 | | LockGuarded<Data> d_data; |
393 | | #endif |
394 | | }; |
395 | | |
396 | | std::vector<uint8_t> makeEDNSTraceParentOption(const std::shared_ptr<Tracer>& tracer); |
397 | | bool addTraceparentEdnsOptionToPacketBuffer(PacketBuffer& origBuf, const std::shared_ptr<Tracer>& tracer, const size_t qnameWireLength, const size_t proxyProtocolPayloadSize, const uint16_t traceparentOptionCode = EDNSOptionCode::TRACEPARENT, const bool isTCP = false); |
398 | | |
399 | | /* |
400 | | * @brief Use this to *maybe* get an Internal Kind Closer in the current scope |
401 | | * |
402 | | * @param tracer A shared_ptr to a Tracer, if it is a nullptr, the returned closer is a nullopt |
403 | | * @param spanName The name of the span |
404 | | */ |
405 | | std::optional<pdns::trace::dnsdist::Tracer::Closer> getCloserForInternalSpan([[maybe_unused]] std::shared_ptr<pdns::trace::dnsdist::Tracer>& tracer, [[maybe_unused]] const std::string& spanName); |
406 | | |
407 | | /* |
408 | | * @brief sends the protobuf for tracer to remoteloggers |
409 | | */ |
410 | | void sendTracesToRemoteLoggers(const std::shared_ptr<Tracer>& tracer, const std::vector<std::shared_ptr<RemoteLoggerInterface>>& remoteloggers); |
411 | | } // namespace pdns::trace::dnsdist |