]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
feat(dnsdist): add named maintenance callbacks
authorPieter Lexis <pieter.lexis@powerdns.com>
Wed, 1 Apr 2026 13:35:22 +0000 (15:35 +0200)
committerPieter Lexis <pieter.lexis@powerdns.com>
Mon, 1 Jun 2026 10:51:43 +0000 (12:51 +0200)
pdns/dnsdistdist/dnsdist-lua-hooks.cc
pdns/dnsdistdist/dnsdist-lua-hooks.hh
pdns/dnsdistdist/docs/reference/config.rst
regression-tests.dnsdist/test_OpenTelemetryTracing.py

index 7080f0cd6b4cf5eea946c5d370af5f450dc99185..c23e6d58d8d09aae142f2042dbb2bad63f9cbb0a 100644 (file)
@@ -1,8 +1,10 @@
 
 #include "dnsdist-lua-hooks.hh"
 #include "dnsdist-lua.hh"
+#include "dnsdist-opentelemetry.hh"
 #include "lock.hh"
 #include "tcpiohandler.hh"
+#include <memory>
 
 namespace dnsdist::lua::hooks
 {
@@ -12,21 +14,22 @@ using TicketsKeyAddedHook = std::function<void(const std::string&, size_t)>;
 using ServerStateChangeCallback = std::function<void(const std::string&, bool)>;
 
 static LockGuarded<std::vector<ExitCallback>> s_exitCallbacks;
-static LockGuarded<std::vector<MaintenanceCallback>> s_maintenanceHooks;
+static LockGuarded<std::vector<std::pair<std::string, MaintenanceCallback>>> s_maintenanceHooks;
 static LockGuarded<std::vector<ServerStateChangeCallback>> s_serverStateChangeHooks;
 
-void runMaintenanceHooks(const LuaContext& context)
+void runMaintenanceHooks(const LuaContext& context, std::shared_ptr<pdns::trace::dnsdist::Tracer>& tracer)
 {
   (void)context;
   for (const auto& callback : *(s_maintenanceHooks.lock())) {
-    callback();
+    pdns::trace::dnsdist::getCloserForInternalSpan(tracer, callback.first);
+    callback.second();
   }
 }
 
-static void addMaintenanceCallback(const LuaContext& context, MaintenanceCallback callback)
+static void addMaintenanceCallback(const LuaContext& context, MaintenanceCallback callback, std::string name = "")
 {
   (void)context;
-  s_maintenanceHooks.lock()->push_back(std::move(callback));
+  s_maintenanceHooks.lock()->push_back({"maintenanceCallback/" + name, std::move(callback)});
 }
 
 void clearMaintenanceHooks()
@@ -89,9 +92,9 @@ void clearServerStateChangeCallbacks()
 
 void setupLuaHooks(LuaContext& luaCtx)
 {
-  luaCtx.writeFunction("addMaintenanceCallback", [&luaCtx](const MaintenanceCallback& callback) {
+  luaCtx.writeFunction("addMaintenanceCallback", [&luaCtx](const MaintenanceCallback& callback, const std::optional<std::string> name) {
     setLuaSideEffect();
-    addMaintenanceCallback(luaCtx, callback);
+    addMaintenanceCallback(luaCtx, callback, name.value_or("unnamed"));
   });
   luaCtx.writeFunction("addExitCallback", [&luaCtx](const ExitCallback& callback) {
     setLuaSideEffect();
index 89a4d485e7f751948080651e55c2b4bcf5525f23..83d87718b9f2f3f674caf7f5d5df3ea164c94e7f 100644 (file)
  */
 #pragma once
 
-#include <functional>
+#include "dnsdist-opentelemetry.hh"
+#include <memory>
 #include <string>
 
 class LuaContext;
 
 namespace dnsdist::lua::hooks
 {
-void runMaintenanceHooks(const LuaContext& context);
+void runMaintenanceHooks(const LuaContext& context, std::shared_ptr<pdns::trace::dnsdist::Tracer>& tracer);
 void clearMaintenanceHooks();
 void runExitCallbacks(const LuaContext& context);
 void clearExitCallbacks();
index abb2178358d8b014912a6e971a4fa739c8e2d169..05addaafe6ea9559fd35ab1fc3252b683b8d6cf9 100644 (file)
@@ -2173,14 +2173,17 @@ Other functions
 
   :param function callback: The function to be called. It takes no parameter and returns no value.
 
-.. function:: addMaintenanceCallback(callback)
+.. function:: addMaintenanceCallback(callback[, name])
 
   .. versionadded:: 1.9.0
+  .. versionchanged:: 2.2.0
+    ``name`` parameter added.
 
   Register a Lua function to be called as part of the ``maintenance`` hook, which is executed roughly every second.
   The function should not block for a long period of time, as it would otherwise delay the execution of the other functions registered for this hook, as well as the execution of the :func:`maintenance` function.
 
   :param function callback: The function to be called. It takes no parameter and returns no value.
+  :param string name: The name of the callback, currently only exposed in :doc:`OpenTelemetry traces <ottrace>`.
 
   .. code-block:: lua
 
index 9ace75a4c75712f0e6c6b8246aa49898b4dcfceb..6978de8a91b79e21f8080206e4d40a9fb8ba877a 100644 (file)
@@ -2,6 +2,7 @@
 
 import base64
 import binascii
+import pprint
 import threading
 import time
 
@@ -71,6 +72,7 @@ class DNSDistOpenTelemetryProtobufTest(test_Protobuf.DNSDistProtobufTest):
         return self.getFirstProtobufMessage(timeout=1)
 
     def checkOTDataBase(self, otData):
+        pprint.pprint(otData)
         self.assertEqual(len(otData["resource_spans"]), 1)
         self.assertEqual(len(otData["resource_spans"][0]["resource"]["attributes"]), 1)
 
@@ -1281,12 +1283,12 @@ class TestOpenTelemetryTracingInternalBase(DNSDistOpenTelemetryProtobufTest):
             return result[0]
         raise KeyError(f"{name} not found in OT Data")
 
-    def checkMaintenanceSpanNames(self, all_span_name, extra_names=set()):
+    def checkMaintenanceSpanNames(self, all_span_name, extra_names=set(), callback_names=set()):
         all_names = {
             "maintenanceThread",
             "maintenanceHooks",
             "DynamicBlocks::runRegisteredGroups",
-        }.union(extra_names)
+        }.union(extra_names).union({f"maintenanceCallback/{name}" for name in callback_names})
 
         self.assertSetEqual(all_span_name, all_names)
 
@@ -1434,6 +1436,61 @@ addMaintenanceCallback(my_maintenance)
             {
                 "my-span",
             },
+            {
+                "unnamed",
+            },
+        )
+
+        maintenanceFunction_span = self.getSpan(otData, "maintenanceHooks")
+        self.assertListEqual(
+            maintenanceFunction_span["attributes"],
+            [{"key": "outside", "value": {"string_value": "hello from the outside"}}],
+        )
+
+        my_span = self.getSpan(otData, "my-span")
+        self.assertListEqual(
+            my_span["attributes"],
+            [{"key": "inside", "value": {"string_value": "hello from the inside"}}],
+        )
+
+
+class TestOpenTelemetryTracingInternalWithFunctionsNamedCallbackLua(TestOpenTelemetryTracingInternalBase):
+    _config_template = """
+local function my_maintenance()
+       setSpanAttribute("outside", "hello from the outside")
+       withTraceSpan("my-span", function()
+               setSpanAttribute("inside", "hello from the inside")
+               os.execute("sleep 0.1")
+       end)
+end
+
+newServer{address="127.0.0.1:%d"}
+getServer(0):setUp()
+rl = newRemoteLogger('127.0.0.1:%d')
+setOpenTelemetryTracing(true)
+setOpenTelemetryInternalTrace('maintenance', {rl}, 60)
+
+addMaintenanceCallback(my_maintenance, "my_maintenance")
+"""
+
+    _config_params = [
+        "_testServerPort",
+        "_protobufServerPort",
+    ]
+
+    def testMaintenance(self):
+        otData = self.getFirstMaintenanceProtobufMessage()
+
+        msg_span_name = {v["name"] for v in otData["resource_spans"][0]["scope_spans"][0]["spans"]}
+
+        self.checkMaintenanceSpanNames(
+            msg_span_name,
+            {
+                "my-span",
+            },
+            {
+                "my_maintenance",
+            },
         )
 
         maintenanceFunction_span = self.getSpan(otData, "maintenanceHooks")