]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
feat(dnsdist): Add Query info to the root span
authorPieter Lexis <pieter.lexis@powerdns.com>
Tue, 14 Oct 2025 11:11:05 +0000 (13:11 +0200)
committerPieter Lexis <pieter.lexis@powerdns.com>
Tue, 14 Oct 2025 19:00:01 +0000 (21:00 +0200)
pdns/dnsdistdist/dnsdist-actions-factory.cc
pdns/dnsdistdist/dnsdist-opentelemetry.cc
pdns/dnsdistdist/dnsdist-opentelemetry.hh
regression-tests.dnsdist/test_OpenTelemetryTracing.py

index de4704a820790ef32fca6497839538640e9b8063..fdfe8ef092a4293a7fd7de7a2d176ab5e7c0a4fc 100644 (file)
@@ -1681,9 +1681,8 @@ public:
   SetTraceAction(bool value) :
     d_value{value} {};
 
-  DNSAction::Action operator()([[maybe_unused]] DNSQuestion* dnsquestion, std::string* ruleresult) const override
+  DNSAction::Action operator()([[maybe_unused]] DNSQuestion* dnsquestion, [[maybe_unused]] std::string* ruleresult) const override
   {
-    (void)ruleresult;
 #ifndef DISABLE_PROTOBUF
     auto tracer = dnsquestion->ids.getTracer();
     if (tracer == nullptr) {
@@ -1692,9 +1691,10 @@ public:
     }
     if (d_value) {
       tracer->activate();
-      tracer->setTraceAttribute("query.qname", AnyValue{dnsquestion->ids.qname.toStringNoDot()});
-      tracer->setTraceAttribute("query.qtype", AnyValue{QType(dnsquestion->ids.qtype).toString()});
-      tracer->setTraceAttribute("query.remote", AnyValue{dnsquestion->ids.origRemote.toLogString()});
+      tracer->setRootSpanAttribute("query.qname", AnyValue{dnsquestion->ids.qname.toStringNoDot()});
+      tracer->setRootSpanAttribute("query.qtype", AnyValue{QType(dnsquestion->ids.qtype).toString()});
+      tracer->setRootSpanAttribute("query.remote.address", AnyValue{dnsquestion->ids.origRemote.toString()});
+      tracer->setRootSpanAttribute("query.remote.port", AnyValue{dnsquestion->ids.origRemote.getPort()});
     }
     else {
       tracer->deactivate();
index 1cbb4ba1c33bd9add11d5df6329d04972cafb3ce..60c64ac2d0ed72e32329a1c8cd999a6c7d292763 100644 (file)
@@ -70,8 +70,17 @@ TracesData Tracer::getTracesData()
           .start_time_unix_nano = preActivationTrace.start_time_unix_nano,
           .end_time_unix_nano = preActivationTrace.end_time_unix_nano,
         });
+
+      if (preActivationTrace.parent_span_id == pdns::trace::s_emptySpanID) {
+        // This is the root span
+        otTrace.resource_spans.at(0).scope_spans.at(0).spans.back().attributes.insert(
+          otTrace.resource_spans.at(0).scope_spans.at(0).spans.back().attributes.cend(),
+          d_rootSpanAttributes.begin(),
+          d_rootSpanAttributes.end());
+      }
     }
   }
+
   {
     auto lockedPost = d_postActivationSpans.read_only_lock();
     otTrace.resource_spans.at(0).scope_spans.at(0).spans.insert(
@@ -178,6 +187,16 @@ void Tracer::closeSpan([[maybe_unused]] const SpanID& spanID)
 #endif
 }
 
+void Tracer::setRootSpanAttribute([[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value)
+{
+#ifndef DISABLE_PROTOBUF
+  d_rootSpanAttributes.push_back({
+    .key = key,
+    .value = value,
+  });
+#endif
+}
+
 // TODO: Figure out what to do with duplicate keys
 void Tracer::setSpanAttribute([[maybe_unused]] const SpanID& spanid, [[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value)
 {
index e4480f6a3e0b6081c39f80fe95a60875fec779d9..aa3de14ed8263551ecba9602dc30a43a5a1409a6 100644 (file)
@@ -117,6 +117,14 @@ public:
    */
   bool setTraceAttribute(const std::string& key, const AnyValue& value);
 
+  /**
+   * @brief Set an attribute on the root span
+   *
+   * @param key
+   * @param value
+   */
+  void setRootSpanAttribute(const std::string& key, const AnyValue& value);
+
   /**
    * @brief Set an attribute on a Span
    *
@@ -306,6 +314,10 @@ private:
    * @brief All attributes related to this Trace (added to the ScopeSpan)
    */
   std::vector<pdns::trace::KeyValue> d_attributes;
+  /**
+   * @brief All attributes related to the root span of this trace
+   */
+  std::vector<pdns::trace::KeyValue> d_rootSpanAttributes;
 
   /**
    * @brief The TraceID for this Tracer. It is stable for the lifetime of the Tracer
index a2705a95240da57dcd3c1bef964d8960930b7294..5738840f47b341b0f0f96b3ed853b938dd83b70c 100644 (file)
@@ -70,19 +70,40 @@ class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest):
 
         # Ensure the values are correct
         # TODO: query.remote with port
-        msg_scope_attrs = {
-            v["key"]: v["value"]["string_value"]
+        msg_scope_attr_keys = [
+            v["key"]
             for v in ot_data["resource_spans"][0]["scope_spans"][0]["scope"][
                 "attributes"
             ]
-            if v["key"] != "query.remote"
+        ]
+        self.assertListEqual(msg_scope_attr_keys, ["hostname"])
+
+        root_span_attr_keys = [
+            v["key"]
+            for v in ot_data["resource_spans"][0]["scope_spans"][0]["spans"][0][
+                "attributes"
+            ]
+        ]
+        self.assertListEqual(
+            root_span_attr_keys,
+            ["query.qname", "query.qtype", "query.remote.address", "query.remote.port"],
+        )
+
+        # No way to guess the test port, but check the rest of the values
+        root_span_attrs = {
+            v["key"]: v["value"]["string_value"]
+            for v in ot_data["resource_spans"][0]["scope_spans"][0]["spans"][0][
+                "attributes"
+            ]
+            if v["key"] not in ["query.remote.port"]
         }
         self.assertDictEqual(
-            msg_scope_attrs,
             {
                 "query.qname": "query.ot.tests.powerdns.com",
                 "query.qtype": "A",
+                "query.remote.address": "127.0.0.1",
             },
+            root_span_attrs,
         )
 
         msg_span_name = [