-// 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
// 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(
// 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);
}
}
}
-// 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
#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 {
namespace log {
// Forward declarations
-class LoggerSpecification;
struct OutputOption;
/// \brief Logger Manager Implementation
/// 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
///
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
/// \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
-// 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
#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>
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);
+}