summaryrefslogtreecommitdiffstats
path: root/src/lib/log/logger_manager_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/log/logger_manager_impl.cc')
-rw-r--r--src/lib/log/logger_manager_impl.cc306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
new file mode 100644
index 0000000..4b03b8c
--- /dev/null
+++ b/src/lib/log/logger_manager_impl.cc
@@ -0,0 +1,306 @@
+// Copyright (C) 2011-2021 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <algorithm>
+#include <iostream>
+#include <array>
+
+#include <log4cplus/logger.h>
+#include <log4cplus/configurator.h>
+#include <log4cplus/hierarchy.h>
+#include <log4cplus/consoleappender.h>
+#include <log4cplus/fileappender.h>
+#include <log4cplus/syslogappender.h>
+#include <log4cplus/helpers/loglog.h>
+#include <log4cplus/version.h>
+
+#include <log/logger.h>
+#include <log/logger_support.h>
+#include <log/logger_level_impl.h>
+#include <log/logger_manager.h>
+#include <log/logger_manager_impl.h>
+#include <log/log_messages.h>
+#include <log/logger_name.h>
+#include <log/logger_specification.h>
+#include <log/buffer_appender_impl.h>
+
+#include <exceptions/isc_assert.h>
+
+#include <boost/lexical_cast.hpp>
+
+using namespace std;
+using boost::lexical_cast;
+
+namespace isc {
+namespace log {
+
+// Reset hierarchy of loggers back to default settings. This removes all
+// appenders from loggers, sets their severity to NOT_SET (so that events are
+// passed back to the parent) and resets the root logger to logging
+// informational messages. (This last is not a log4cplus default, so we have to
+// explicitly reset the logging severity.)
+void
+LoggerManagerImpl::processInit() {
+ storeBufferAppenders();
+
+ log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+ initRootLogger();
+}
+
+// Flush the BufferAppenders at the end of processing a new specification
+void
+LoggerManagerImpl::processEnd() {
+ flushBufferAppenders();
+}
+
+// Process logging specification. Set up the common states then dispatch to
+// add output specifications.
+void
+LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
+ log4cplus::Logger logger = log4cplus::Logger::getInstance(
+ expandLoggerName(spec.getName()));
+
+ // Set severity level according to specification entry.
+ logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
+ Level(spec.getSeverity(), spec.getDbglevel())));
+
+ // Set the additive flag.
+ logger.setAdditivity(spec.getAdditive());
+
+ // 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);
+ }
+ }
+ }
+}
+
+// Console appender - log to either stdout or stderr.
+void
+LoggerManagerImpl::createConsoleAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ log4cplus::SharedAppenderPtr console(
+ new log4cplus::ConsoleAppender(
+ (opt.stream == OutputOption::STR_STDERR), opt.flush));
+
+ setAppenderLayout(console, (opt.pattern.empty() ?
+ OutputOption::DEFAULT_CONSOLE_PATTERN : opt.pattern));
+ logger.addAppender(console);
+}
+
+// File appender. Depending on whether a maximum size is given, either
+// a standard file appender or a rolling file appender will be created.
+// In the case of the latter, we set "UseLockFile" to true so that
+// log4cplus internally avoids race in rolling over the files by multiple
+// processes. This feature isn't supported in log4cplus 1.0.x, but setting
+// the property unconditionally is okay as unknown properties are simply
+// ignored.
+void
+LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ // Append to existing file
+ const std::ios::openmode mode = std::ios::app;
+
+ log4cplus::SharedAppenderPtr fileapp;
+ if (opt.maxsize == 0) {
+ fileapp = log4cplus::SharedAppenderPtr(new log4cplus::FileAppender(
+ opt.filename, mode, opt.flush));
+ } else {
+ log4cplus::helpers::Properties properties;
+ properties.setProperty("File", opt.filename);
+
+ // log4cplus supports file sizes past INT_MAX only in suffixed format.
+ // Convert from integer.
+ uint64_t maxsize(opt.maxsize);
+ size_t i(0);
+ while (std::numeric_limits<int32_t>::max() < maxsize && i < 2) {
+ maxsize /= 1000;
+ ++i;
+ }
+ std::array<std::string, 3> const suffixes({"", "KB", "MB"});
+ std::string const max_file_size(to_string(maxsize) + suffixes[i]);
+
+ // If maxsize is still past INT_MAX, it will overflow in log4cplus,
+ // so stop here instead.
+ if (std::numeric_limits<int32_t>::max() < maxsize) {
+ isc_throw(BadValue, "expected maxsize < "
+ << std::numeric_limits<int32_t>::max()
+ << "MB, but instead got " << max_file_size);
+ }
+
+ properties.setProperty("MaxFileSize", max_file_size);
+ properties.setProperty("MaxBackupIndex",
+ lexical_cast<string>(opt.maxver));
+ properties.setProperty("ImmediateFlush", opt.flush ? "true" : "false");
+ properties.setProperty("UseLockFile", "true");
+ fileapp = log4cplus::SharedAppenderPtr(
+ new log4cplus::RollingFileAppender(properties));
+ }
+
+ setAppenderLayout(fileapp, (opt.pattern.empty() ?
+ OutputOption::DEFAULT_FILE_PATTERN : opt.pattern));
+ logger.addAppender(fileapp);
+}
+
+void
+LoggerManagerImpl::createBufferAppender(log4cplus::Logger& logger) {
+ log4cplus::SharedAppenderPtr bufferapp(new internal::BufferAppender());
+ bufferapp->setName("buffer");
+ logger.addAppender(bufferapp);
+ // Since we do not know at what level the loggers will end up
+ // running, set it to the highest for now
+ logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
+}
+
+// Syslog appender.
+void
+LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
+ const OutputOption& opt)
+{
+ log4cplus::helpers::Properties properties;
+ properties.setProperty("ident", getRootLoggerName());
+ properties.setProperty("facility", opt.facility);
+ log4cplus::SharedAppenderPtr syslogapp(
+ new log4cplus::SysLogAppender(properties));
+ setAppenderLayout(syslogapp, (opt.pattern.empty() ?
+ OutputOption::DEFAULT_SYSLOG_PATTERN : opt.pattern));
+ logger.addAppender(syslogapp);
+}
+
+
+// One-time initialization of the log4cplus system
+void
+LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel,
+ bool buffer)
+{
+ // Set up basic configurator. This attaches a ConsoleAppender to the
+ // root logger with suitable output. This is used until we we have
+ // actually read the logging configuration, in which case the output
+ // may well be changed.
+ log4cplus::BasicConfigurator config;
+ config.configure();
+
+ // Add the additional debug levels
+ LoggerLevelImpl::init();
+
+ initRootLogger(severity, dbglevel, buffer);
+}
+
+// Reset logging to default configuration. This closes all appenders
+// and resets the root logger to output INFO messages to the console.
+// It is principally used in testing.
+void
+LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel)
+{
+ // Initialize the root logger
+ initRootLogger(severity, dbglevel);
+}
+
+// Initialize the root logger
+void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
+ int dbglevel, bool buffer)
+{
+ log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+
+ // Disable log4cplus' own logging, unless --enable-debug was
+ // specified to configure. Note that this does not change
+ // LogLog's levels (that is still just INFO).
+#ifndef ENABLE_DEBUG
+ log4cplus::helpers::LogLog::getLogLog()->setQuietMode(true);
+#endif
+
+ // Set the log4cplus root to not output anything - effectively we are
+ // ignoring it.
+ log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);
+
+ // Set the level for the Kea root logger to the given severity and
+ // debug level.
+ log4cplus::Logger kea_root = log4cplus::Logger::getInstance(
+ getRootLoggerName());
+ kea_root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
+ Level(severity, dbglevel)));
+
+ if (buffer) {
+ createBufferAppender(kea_root);
+ } else {
+ OutputOption opt;
+ createConsoleAppender(kea_root, opt);
+ }
+}
+
+
+void LoggerManagerImpl::setAppenderLayout(
+ log4cplus::SharedAppenderPtr& appender,
+ std::string pattern)
+{
+ // Finally the text of the message
+ appender->setLayout(
+#if LOG4CPLUS_VERSION < LOG4CPLUS_MAKE_VERSION(2, 0, 0)
+ auto_ptr<log4cplus::Layout>
+#else
+ unique_ptr<log4cplus::Layout>
+#endif
+ (new log4cplus::PatternLayout(pattern)));
+}
+
+void LoggerManagerImpl::storeBufferAppenders() {
+ // Walk through all loggers, and find any buffer appenders there
+ log4cplus::LoggerList loggers = log4cplus::Logger::getCurrentLoggers();
+ log4cplus::LoggerList::iterator it;
+ for (it = loggers.begin(); it != loggers.end(); ++it) {
+ log4cplus::SharedAppenderPtr buffer_appender =
+ it->getAppender("buffer");
+ if (buffer_appender) {
+ buffer_appender_store_.push_back(buffer_appender);
+ }
+ }
+}
+
+void LoggerManagerImpl::flushBufferAppenders() {
+ std::vector<log4cplus::SharedAppenderPtr> copy;
+ buffer_appender_store_.swap(copy);
+
+ std::vector<log4cplus::SharedAppenderPtr>::iterator it;
+ for (it = copy.begin(); it != copy.end(); ++it) {
+ internal::BufferAppender* app =
+ dynamic_cast<internal::BufferAppender*>(it->get());
+ isc_throw_assert(app);
+ app->flush();
+ }
+}
+
+} // namespace log
+} // namespace isc