Coverage Report

Created: 2026-06-09 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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