]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
feat(dnsdist): Allow setting `instance` logging field with the Server ID
authorPieter Lexis <pieter.lexis@powerdns.com>
Fri, 16 Jan 2026 16:12:08 +0000 (17:12 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 19 Jan 2026 10:01:46 +0000 (11:01 +0100)
pdns/dnsdistdist/dnsdist-configuration-yaml.cc
pdns/dnsdistdist/dnsdist-configuration.hh
pdns/dnsdistdist/dnsdist-logging.cc
pdns/dnsdistdist/dnsdist-lua.cc
pdns/dnsdistdist/dnsdist-settings-definitions.yml
pdns/dnsdistdist/docs/reference/config.rst
regression-tests.dnsdist/test_StructuredLogging.py

index 16787b48a482f3ce4ea720c60dc12a227878b13f..188682bcad52ab768c9a1395d9e91ea6fcb8ea3a 100644 (file)
@@ -1012,6 +1012,7 @@ static void handleLoggingConfiguration(const Context& context, const dnsdist::ru
     if (timeFormat) {
       config.d_structuredLoggingTimeFormat = *timeFormat;
     }
+    config.d_structuredLoggingUseServerID = settings.structured.set_instance_from_server_id;
   });
 }
 
index 6e5fc87eb5124446ec0fc772513df02634d231c4..9fc3987bbf904bfc3fddfecb2bf59290e0883fde 100644 (file)
@@ -117,6 +117,7 @@ struct ImmutableConfiguration
   bool d_snmpEnabled{false};
   bool d_snmpTrapsEnabled{false};
   bool d_structuredLogging{false};
+  bool d_structuredLoggingUseServerID{false};
 };
 
 /* this part of the configuration can be updated at runtime via
index 5b007a523e41908e02a527ce73dcab19e7c3c8bd..29be70378e5c17b54b8c0a9b4a7be0978c6084a0 100644 (file)
@@ -95,6 +95,9 @@ static void loggerSDBackend(const Logging::Entry& entry)
   }
   appendKeyAndVal("LEVEL", std::to_string(entry.level));
   appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
+  if (dnsdist::configuration::getImmutableConfiguration().d_structuredLoggingUseServerID) {
+    appendKeyAndVal("INSTANCE", dnsdist::configuration::getCurrentRuntimeConfiguration().d_server_id);
+  }
   if (entry.name) {
     appendKeyAndVal("SUBSYSTEM", entry.name.value());
   }
@@ -142,6 +145,10 @@ static void loggerJSONBackend(const Logging::Entry& entry)
     json.emplace("priority", std::to_string(entry.d_priority));
   }
 
+  if (dnsdist::configuration::getImmutableConfiguration().d_structuredLoggingUseServerID) {
+    json.emplace("instance", dnsdist::configuration::getCurrentRuntimeConfiguration().d_server_id);
+  }
+
   for (auto const& value : entry.values) {
     json.emplace(value.first, value.second);
   }
@@ -170,6 +177,9 @@ static void loggerBackend(const Logging::Entry& entry)
   if (entry.d_priority != 0) {
     buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority));
   }
+  if (dnsdist::configuration::getImmutableConfiguration().d_structuredLoggingUseServerID) {
+    buf << " instance=" << std::quoted(dnsdist::configuration::getCurrentRuntimeConfiguration().d_server_id);
+  }
 
   std::array<char, 64> timebuf{};
   buf << " ts=" << std::quoted(convertTime(entry.d_timestamp, timebuf));
index f4b4e5e0c9a620448801d6f8acf5aac7b7f5cdd6..606c6e16635b3efea0a97f175180d6b8b5fe0db7 100644 (file)
@@ -1761,10 +1761,11 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
            getLogger("setVerboseLogDestination")->error(Logr::Error, e.what(), "Error while opening the verbose logging destination file", "path", Logging::Loggable(dest)));
     }
   });
-  luaCtx.writeFunction("setStructuredLogging", [](bool enable, std::optional<LuaAssociativeTable<std::string>> options) {
+  luaCtx.writeFunction("setStructuredLogging", [](bool enable, std::optional<LuaAssociativeTable<boost::variant<std::string, bool>>> options) {
     std::optional<dnsdist::configuration::TimeFormat> format{};
     std::string timeFormat;
     std::string backend;
+    bool useServerID{false};
     if (options) {
       if (getOptionalValue<std::string>(options, "timeFormat", timeFormat) == 1) {
         if (timeFormat == "numeric") {
@@ -1779,10 +1780,11 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
         }
       }
       getOptionalValue<std::string>(options, "backend", backend);
+      getOptionalValue<bool>(options, "setInstanceFromServerID", useServerID);
       checkAllParametersConsumed("setStructuredLogging", options);
     }
 
-    dnsdist::configuration::updateImmutableConfiguration([enable, &backend, format](dnsdist::configuration::ImmutableConfiguration& config) {
+    dnsdist::configuration::updateImmutableConfiguration([enable, &backend, format, useServerID](dnsdist::configuration::ImmutableConfiguration& config) {
       if (enable && !backend.empty()) {
         config.d_loggingBackend = backend;
       }
@@ -1790,6 +1792,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
         config.d_structuredLoggingTimeFormat = *format;
       }
       config.d_structuredLogging = enable;
+      config.d_structuredLoggingUseServerID = useServerID;
     });
   });
 
index 503ddf870309955e0cfcbf2eba44652ccc49113a..cd1e1b68abac5bacec57e043d470c9a9963c9059 100644 (file)
@@ -1802,11 +1802,17 @@ structured_logging:
       default: ""
       version_added: "2.1.0"
       description: |
-                   The backend used for structured logging output. See :doc:`../advanced/structured-logging-dictionary` for more details. Available backends are:
+        The backend used for structured logging output. See :doc:`../advanced/structured-logging-dictionary` for more details. Available backends are:
 
-                   * ``default``: use the traditional logging system to output structured logging information.
-                   * ``systemd-journal``: use ``systemd-journal``. When using this backend, provide ``-o verbose`` or simular output option to ``journalctl`` to view the full information.
-                   * ``json``: JSON objects are written to the standard error stream.
+        * ``default``: use the traditional logging system to output structured logging information.
+        * ``systemd-journal``: use ``systemd-journal``. When using this backend, provide ``-o verbose`` or simular output option to ``journalctl`` to view the full information.
+        * ``json``: JSON objects are written to the standard error stream.
+    - name: "set_instance_from_server_id"
+      type: "bool"
+      default: "false"
+      description: |
+        Add a field "instance" to each log line with the value of ``general.server_id``.
+      version_added: 2.1.0
 
 logging:
   description: "Logging settings"
index cf04952aafb908d4bb282925bb7c68d8ba8d1bf7..793e751e67e691f517a4b9e4144dc92aca817ed1 100644 (file)
@@ -1404,6 +1404,7 @@ Status, Statistics and More
 
   .. versionchanged:: 2.1.0
     The ``backend`` option has been added.
+    The ``setInstanceFromServerID`` option has been added
     The ``levelPrefix`` option has no longer any effect because it was confusing. The log level is now always logged as ``level`` and the syslog priority, if any, as ``priority`` in all backends except the default one where it is named ``prio``
 
   Set whether log messages should be in structured-logging format. This is turned off by default. See :doc:`../advanced/structured-logging-dictionary` for more details.
@@ -1424,6 +1425,7 @@ Status, Statistics and More
   * ``backend``: string - The backend used for structured logging output, see below. Added in 2.1.0.
   * ``timeFormat=format``: string - Set the time format. Supported values are ``ISO8601`` and ``numeric``. Default is ``numeric``.
   * ``levelPrefix=prefix``: string - Set the prefix for the log level. Default is ``prio``. No longer supported as of 2.1.0.
+  * ``setInstanceFromServerID=false``: bool - Add the "instance" field with the value of the server ID (set with :func:`setServerID`) to each log line. Added in 2.1.0.
 
  Available backends:
 
index 9c9c6f7a6e1ab886510629368ac7b5fdeee1c5c0..cc436eb1d5f5b9eb2a0e0b2494acd542b990952c 100644 (file)
@@ -79,3 +79,79 @@ newServer{address="127.0.0.1:%d"}
 
     def testOK(self):
         pass
+
+
+class TestStructuredLoggingDefaultBackendWithInstanceFromYaml(
+    TestStructuredLoggingDefaultBackendFromYaml
+):
+    _yaml_config_template = """---
+general:
+  server_id: "foobar"
+
+binds:
+  - listen_address: "127.0.0.1:%d"
+    protocol: Do53
+
+backends:
+  - address: "127.0.0.1:%d"
+    protocol: Do53
+
+logging:
+  structured:
+    enabled: true
+    set_instance_from_server_id: true
+"""
+    _checkConfigExpectedOutputPrefix = b'msg="Configuration OK" subsystem="setup"'
+
+
+class TestStructuredLoggingJSONBackendWithInstanceFromYaml(
+    TestStructuredLoggingJSONBackendFromYaml
+):
+    _yaml_config_template = """---
+general:
+  server_id: "foobar"
+
+binds:
+  - listen_address: "127.0.0.1:%d"
+    protocol: Do53
+
+backends:
+  - address: "127.0.0.1:%d"
+    protocol: Do53
+
+logging:
+  structured:
+    enabled: true
+    backend: "json"
+    set_instance_from_server_id: true
+"""
+    _checkConfigExpectedOutputPrefix = (
+        b'{"instance": "foobar", "level": "0", "msg": "Configuration OK", "path":'
+    )
+
+
+class TestStructuredLoggingDefaultBackendWithInstanceFromLua(
+    TestStructuredLoggingDefaultBackendFromLua
+):
+    _config_template = """
+setServerID("foobar")
+setStructuredLogging(true, {setInstanceFromServerID=true})
+
+newServer{address="127.0.0.1:%d"}
+"""
+    _checkConfigExpectedOutputPrefix = b'msg="Configuration OK" subsystem="setup" level="0" prio="Info" instance="foobar" ts='
+
+
+class TestStructuredLoggingJSONBackendWithInstanceFromLua(
+    TestStructuredLoggingJSONBackendFromLua
+):
+
+    _config_template = """
+setServerID("foobar")
+setStructuredLogging(true, {backend="json", setInstanceFromServerID=true})
+
+newServer{address="127.0.0.1:%d"}
+"""
+    _checkConfigExpectedOutputPrefix = (
+        b'{"instance": "foobar", "level": "0", "msg": "Configuration OK", "path":'
+    )