]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2537] add inheritance of output_options
authorAndrei Pavel <andrei@isc.org>
Fri, 19 Aug 2022 10:06:55 +0000 (13:06 +0300)
committerAndrei Pavel <andrei@isc.org>
Fri, 26 Aug 2022 15:39:15 +0000 (18:39 +0300)
Unlike severity and debug level that have inheritance support embedded
in log4cplus, output_options do not. This commit adds support for this
inheritance by storing a copy of the root logger's specification and
using it on child loggers if they don't have a specification themselves.

src/lib/log/logger_manager_impl.cc
src/lib/log/logger_manager_impl.h
src/lib/log/tests/logger_manager_unittest.cc

index 4b03b8ceab6e87274eba510a1b363002441dcef4..0cb5e94c96734b3c192c47ff0d05b62fa8660231 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -62,8 +62,10 @@ LoggerManagerImpl::processEnd() {
 // add output specifications.
 void
 LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
-    log4cplus::Logger logger = log4cplus::Logger::getInstance(
-                                   expandLoggerName(spec.getName()));
+    string const& name(spec.getName());
+    string const& root_logger_name(getRootLoggerName());
+
+    log4cplus::Logger logger = log4cplus::Logger::getInstance(expandLoggerName(name));
 
     // Set severity level according to specification entry.
     logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
@@ -72,36 +74,48 @@ LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
     // Set the additive flag.
     logger.setAdditivity(spec.getAdditive());
 
+    // Replace all appenders for this logger.
+    logger.removeAllAppenders();
+
+    if (name == root_logger_name) {
+        // Store a copy of the root specification. It might be required later.
+        root_spec_ = spec;
+    }
+
     // Output options given?
     if (spec.optionCount() > 0) {
-        // Replace all appenders for this logger.
-        logger.removeAllAppenders();
-
-        // Now process output specifications.
-        for (LoggerSpecification::const_iterator i = spec.begin();
-             i != spec.end(); ++i) {
-            switch (i->destination) {
-            case OutputOption::DEST_CONSOLE:
-                createConsoleAppender(logger, *i);
-                break;
-
-            case OutputOption::DEST_FILE:
-                createFileAppender(logger, *i);
-                break;
-
-            case OutputOption::DEST_SYSLOG:
-                createSyslogAppender(logger, *i);
-                break;
-
-            default:
-                // Not a valid destination.  As we are in the middle of updating
-                // logging destinations, we could be in the situation where
-                // there are no valid appenders.  For this reason, throw an
-                // exception.
-                isc_throw(UnknownLoggingDestination,
-                          "Unknown logging destination, code = " <<
-                          i->destination);
-            }
+        // If there are output options provided, continue with the given spec.
+        appenderFactory(logger, spec);
+    } else {
+        // If there are no output options, inherit them from the root logger.
+        appenderFactory(logger, root_spec_);
+    }
+}
+
+void
+LoggerManagerImpl::appenderFactory(log4cplus::Logger& logger,
+                                   LoggerSpecification const& spec) {
+    for (OutputOption const& i : spec) {
+        switch (i.destination) {
+        case OutputOption::DEST_CONSOLE:
+            createConsoleAppender(logger, i);
+            break;
+
+        case OutputOption::DEST_FILE:
+            createFileAppender(logger, i);
+            break;
+
+        case OutputOption::DEST_SYSLOG:
+            createSyslogAppender(logger, i);
+            break;
+
+        default:
+            // Not a valid destination.  As we are in the middle of updating
+            // logging destinations, we could be in the situation where
+            // there are no valid appenders.  For this reason, throw an
+            // exception.
+            isc_throw(UnknownLoggingDestination,
+                      "Unknown logging destination, code = " << i.destination);
         }
     }
 }
index 7f1ddb5734bae00715f443e379cad8c5878f300e..c459cfe646600742e7f828cba1e7f90acfb51d2f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,6 +11,7 @@
 
 #include <log4cplus/appender.h>
 #include <log/logger_level.h>
+#include <log/logger_specification.h>
 
 // Forward declaration to avoid need to include log4cplus header file here.
 namespace log4cplus {
@@ -22,7 +23,6 @@ namespace isc {
 namespace log {
 
 // Forward declarations
-class LoggerSpecification;
 struct OutputOption;
 
 /// \brief Logger Manager Implementation
@@ -57,7 +57,7 @@ public:
     /// Processes the specification for a single logger.
     ///
     /// \param spec Logging specification for this logger
-    static void processSpecification(const LoggerSpecification& spec);
+    void processSpecification(const LoggerSpecification& spec);
 
     /// \brief End Processing
     ///
@@ -94,6 +94,16 @@ public:
                       int dbglevel = 0);
 
 private:
+    /// @brief Decides what appender to create.
+    ///
+    /// Delegates to the other functions that create appenders based on what's
+    /// in spec.
+    ///
+    /// @param logger log4cplus logger to which the appender must be attached
+    /// @param spec the configured specification consisting of output options
+    static void appenderFactory(log4cplus::Logger& logger,
+                                LoggerSpecification const& spec);
+
     /// \brief Create console appender
     ///
     /// Creates an object that, when attached to a logger, will log to one
@@ -171,10 +181,14 @@ private:
     /// \c storeBufferAppenders(), and clears it
     void flushBufferAppenders();
 
-    /// Only used between processInit() and processEnd(), to temporarily
+    /// @brief Only used between processInit() and processEnd(), to temporarily
     /// store the buffer appenders in order to flush them after
     /// processSpecification() calls have been completed
     std::vector<log4cplus::SharedAppenderPtr> buffer_appender_store_;
+
+    /// @brief A hard copy of the specification for the root logger used for
+    /// inheritance by child loggers.
+    LoggerSpecification root_spec_;
 };
 
 } // namespace log
index 3a5b76fa16ee8aa2af5724f250b9301a6493f12d..6bde1db12b9ad9e72efe135815df1b6548aa85ba 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -25,6 +25,7 @@
 #include <log/logger.h>
 #include <log/logger_level.h>
 #include <log/logger_manager.h>
+#include <log/logger_name.h>
 #include <log/logger_specification.h>
 #include <log/message_initializer.h>
 #include <log/output_option.h>
@@ -444,3 +445,67 @@ TEST_F(LoggerManagerTest, logDuplicatedMessages) {
     LoggerManager::logDuplicatedMessages();
     ASSERT_EQ(0, MessageInitializer::getDuplicates().size());
 }
+
+// Check that output options can be inherited.
+TEST_F(LoggerManagerTest, outputOptionsInheritance) {
+    LoggerManager manager;
+    SpecificationForFileLogger file_spec;
+    vector<LoggerSpecification> specs;
+
+    // Create the root logger configuration with a file output option.
+    string root_name(getRootLoggerName());
+    LoggerSpecification root_spec(root_name);
+    OutputOption root_option;
+    root_option.destination = OutputOption::DEST_FILE;
+    root_option.filename = file_spec.getFileName();
+    root_option.pattern = "%p %m\n";
+    root_spec.addOutputOption(root_option);
+    specs.push_back(root_spec);
+
+    // Create a child logger configuration without any output options.
+    // It should inherit the output option from the root logger.
+    string foo_name(root_name + ".foo");
+    LoggerSpecification foo_spec(foo_name);
+    specs.push_back(foo_spec);
+
+    // Create another child logger configuration with a console output option.
+    string bar_name(root_name + ".bar");
+    LoggerSpecification bar_spec(bar_name);
+    OutputOption bar_option;
+    bar_option.destination = OutputOption::DEST_CONSOLE;
+    bar_option.pattern = "%p %m\n";
+    bar_spec.addOutputOption(bar_option);
+    specs.push_back(bar_spec);
+
+    // Check the number of output options for each specification.
+    EXPECT_EQ(root_spec.optionCount(), 1);
+    EXPECT_EQ(foo_spec.optionCount(), 0);
+    EXPECT_EQ(bar_spec.optionCount(), 1);
+
+    // Process all the specifications.
+    manager.process(specs.begin(), specs.end());
+
+    // Log two messages each.
+    Logger root_logger(root_name.c_str());
+    Logger foo_logger(foo_name.c_str());
+    Logger bar_logger(bar_name.c_str());
+    LOG_INFO(root_logger, "from root logger 1");
+    LOG_INFO(foo_logger, "from foo logger 1");
+    LOG_INFO(bar_logger, "from bar logger 1");
+    LOG_INFO(root_logger, "from root logger 2");
+    LOG_INFO(foo_logger, "from foo logger 2");
+    LOG_INFO(bar_logger, "from bar logger 2");
+
+    // Check that root and foo were logged to file and that bar which
+    // had an explicit console configuration did not.
+    std::ifstream ifs(file_spec.getFileName());
+    std::stringstream s;
+    s << ifs.rdbuf();
+    std::string const result(s.str());
+    EXPECT_NE(result.find("INFO from root logger 1"), string::npos);
+    EXPECT_NE(result.find("INFO from foo logger 1"), string::npos);
+    EXPECT_EQ(result.find("INFO from bar logger 1"), string::npos);
+    EXPECT_NE(result.find("INFO from root logger 2"), string::npos);
+    EXPECT_NE(result.find("INFO from foo logger 2"), string::npos);
+    EXPECT_EQ(result.find("INFO from bar logger 2"), string::npos);
+}