2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

[#2537] add inheritance of output_options

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.
This commit is contained in:
Andrei Pavel
2022-08-19 13:06:55 +03:00
parent fd1d47e7a5
commit 132f5e1fe2
3 changed files with 125 additions and 32 deletions

View 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();
// 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_);
}
}
// 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;
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_FILE:
createFileAppender(logger, i);
break;
case OutputOption::DEST_SYSLOG:
createSyslogAppender(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);
}
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);
}
}
}

View 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

View 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);
}