BIND 10 trac555, updated. acfbd3b209c50aeff46fc09f9d792a457f51722d [trac555] Get root logger name from global setting

BIND 10 source code commits bind10-changes at lists.isc.org
Thu May 26 15:09:31 UTC 2011


The branch, trac555 has been updated
       via  acfbd3b209c50aeff46fc09f9d792a457f51722d (commit)
       via  9e48d735ae41dd79b6ff856ea4e5ba79112f440c (commit)
       via  cfa31a99243065c40dc3f5a6393bb8ec923d2bd2 (commit)
      from  fae2d0d5854141a9af7c1177c663a0910ab18ad0 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit acfbd3b209c50aeff46fc09f9d792a457f51722d
Author: Stephen Morris <stephen at isc.org>
Date:   Thu May 26 16:08:24 2011 +0100

    [trac555] Get root logger name from global setting
    
    The root logger name is now obtained from the global variable
    accessed by get/setRootLoggerName().
    
    Also, the console appender now provides the correct pattern.

commit 9e48d735ae41dd79b6ff856ea4e5ba79112f440c
Author: Stephen Morris <stephen at isc.org>
Date:   Thu May 26 15:50:30 2011 +0100

    [trac555] Move logging initialization to LoggerManager
    
    This is the global object that controls the logging system, so
    it is logical that initialization is done here instead of in the
    Logger class (which controls the operation of one logger).

commit cfa31a99243065c40dc3f5a6393bb8ec923d2bd2
Author: Stephen Morris <stephen at isc.org>
Date:   Thu May 26 14:13:34 2011 +0100

    [trac555] Checkpoint
    
    Added basic logger manager and its implementation, plus an untested
    console appender.  Also modified logger_support_test to use that
    appender and have got part way through creating a file logger test.

-----------------------------------------------------------------------

Summary of changes:
 src/lib/log/Makefile.am                      |    1 +
 src/lib/log/logger_impl.cc                   |   57 +--------
 src/lib/log/logger_impl.h                    |   17 ---
 src/lib/log/logger_manager.cc                |  102 +++++++++++++++-
 src/lib/log/logger_manager.h                 |   36 +++++-
 src/lib/log/logger_manager_impl.cc           |  176 ++++++++++++++++++++++++++
 src/lib/log/logger_manager_impl.h            |   81 ++++++++++++-
 src/lib/log/logger_specification.h           |   38 +++---
 src/lib/log/logger_support.cc                |   88 +-------------
 src/lib/log/logger_support.h                 |    7 +-
 src/lib/log/tests/Makefile.am                |    1 +
 src/lib/log/tests/logger_manager_unittest.cc |   57 ++++++++-
 src/lib/log/tests/logger_support_test.cc     |  155 ++++++++++++++++-------
 13 files changed, 584 insertions(+), 232 deletions(-)
 create mode 100644 src/lib/log/logger_manager_impl.cc

-----------------------------------------------------------------------
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 24a348b..eb6ec50 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -15,6 +15,7 @@ liblog_la_SOURCES += logger_level.h
 liblog_la_SOURCES += logger_level.h
 liblog_la_SOURCES += logger_level_impl.cc logger_level_impl.h
 liblog_la_SOURCES += logger_manager.cc logger_manager.h
+liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h
 liblog_la_SOURCES += logger_specification.h
 liblog_la_SOURCES += logger_support.cc logger_support.h
 liblog_la_SOURCES += macros.h
diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc
index 3cbfb6b..9ae3ed2 100644
--- a/src/lib/log/logger_impl.cc
+++ b/src/lib/log/logger_impl.cc
@@ -33,7 +33,7 @@
 
 #include <util/strutil.h>
 
-// Note: as log4cplus and th3e BIND 10 logger have many concepts in common, and
+// Note: as log4cplus and the BIND 10 logger have many concepts in common, and
 // thus many similar names, to disambiguate types we don't "use" the log4cplus
 // namespace: instead, all log4cplus types are explicitly qualified.
 
@@ -45,9 +45,6 @@ namespace log {
 // Constructor
 LoggerImpl::LoggerImpl(const string& name)
 {
-    // Initialize log4cplus if not already done
-    initLog4cplus();
-
     // Are we the root logger?
     if (name == getRootLoggerName()) {
         name_ = name;
@@ -133,64 +130,12 @@ LoggerImpl::outputRaw(const Severity& severity, const string& message) {
     }
 }
 
-// Initialization.  This is one initialization for all loggers, so requires
-// a singleton to hold the initialization flag.  The flag is held within a
-// static method to ensure that it is created (and initialized) when needed.
-// This avoids a static initialization fiasco.
-
-bool&
-LoggerImpl::initialized() {
-    static bool initialized = false;
-    return (initialized);
-}
-
-void
-LoggerImpl::initLog4cplus() {
-
-    if (! initialized()) {
-
-        // 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();
-        setRootAppenderLayout();
-
-        // Add additional debug levels
-        LoggerLevelImpl::init();
-
-        // All done.
-        initialized() = true;
-    }
-}
-
-void LoggerImpl::setRootAppenderLayout() {
-
-    // Create the pattern we want for the output - local time.
-    string pattern = "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [";
-    pattern += getRootLoggerName() + string(".%c] %m\n");
-
-    // Retrieve the appenders on the root instance and set the layout to
-    // use that pattern.
-    log4cplus::SharedAppenderPtrList list =
-        log4cplus::Logger::getRoot().getAllAppenders();
-
-    for (log4cplus::SharedAppenderPtrList::iterator i = list.begin();
-         i != list.end(); ++i) {
-        auto_ptr<log4cplus::Layout> layout(
-            new log4cplus::PatternLayout(pattern));
-        (*i)->setLayout(layout);
-    }
-}
-
 // Reset.  Just reset logger hierarchy to default settings (don't remove the
 // loggers - this appears awkward); this is effectively the same as removing
 // them.
 void
 LoggerImpl::reset() {
     log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
-    initialized() = false;
 
     // N.B.  The documentation is not clear, but it does not appear that the
     // methods used to format the new logging levels are removed from the
diff --git a/src/lib/log/logger_impl.h b/src/lib/log/logger_impl.h
index f655c55..68ae807 100644
--- a/src/lib/log/logger_impl.h
+++ b/src/lib/log/logger_impl.h
@@ -192,23 +192,6 @@ public:
 
 
 private:
-
-    /// \brief Initialize log4cplus
-    ///
-    /// Static method to perform initialization of the log4cplus system.
-    static void initLog4cplus();
-
-    /// \brief Initialization Flag
-    ///
-    /// Static method to access an initialization flag.  Doing it this
-    /// way means that there is no static initialization fiasco.
-    static bool& initialized();
-
-    /// \brief Set layout pattern
-    ///
-    /// Sets the layout for root logger appender(s)
-    static void setRootAppenderLayout();
-
     std::string         name_;              ///< Full name of this logger
     log4cplus::Logger   logger_;            ///< Underlying log4cplus logger
 };
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
index e86275e..c89a46b 100644
--- a/src/lib/log/logger_manager.cc
+++ b/src/lib/log/logger_manager.cc
@@ -12,15 +12,29 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <algorithm>
+#include <vector>
+
+#include <log/logger.h>
 #include <log/logger_manager_impl.h>
 #include <log/logger_manager.h>
+#include <log/messagedef.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_initializer.h>
+#include <log/message_reader.h>
+#include <log/message_types.h>
+#include <log/root_logger_name.h>
+#include <log/macros.h>
+#include <log/messagedef.h>
+#include <log/message_initializer.h>
+
+using namespace std;
 
 namespace isc {
 namespace log {
 
-void LoggerManagerImpl::processInit() {}
 void LoggerManagerImpl::processEnd() {}
-void LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {}
 
 // Constructor - create the implementation  class.
 LoggerManager::LoggerManager() {
@@ -38,7 +52,7 @@ LoggerManager::processInit() {
     impl_->processInit();
 }
 
-// Process loggging specification
+// Process logging specification
 void
 LoggerManager::processSpecification(const LoggerSpecification& spec) {
     impl_->processSpecification(spec);
@@ -50,5 +64,87 @@ LoggerManager::processEnd() {
     impl_->processEnd();
 }
 
+
+/// Logging system initialization
+
+void
+LoggerManager::init(const std::string& root, const char* file,
+                    isc::log::Severity severity, int dbglevel)
+{
+    // Create the BIND 10 root logger and set the default severity and
+    // debug level.  This is the logger that has the name of the application.
+    // All other loggers created in this application will be its children.
+    setRootLoggerName(root);
+
+    // Initialize the implementation logging.
+    LoggerManagerImpl::init(severity, dbglevel);
+
+    // TODO: sort out the names.
+    Logger logger("log");
+
+    // Check if there were any duplicate message IDs in the default dictionary
+    // and if so, log them.  Log using the logging facility root logger.
+    vector<string>& duplicates = MessageInitializer::getDuplicates();
+    if (!duplicates.empty()) {
+
+        // There are - sort and remove any duplicates.
+        sort(duplicates.begin(), duplicates.end());
+        vector<string>::iterator new_end =
+            unique(duplicates.begin(), duplicates.end());
+        for (vector<string>::iterator i = duplicates.begin(); i != new_end; ++i) {
+            LOG_WARN(logger, MSG_DUPMSGID).arg(*i);
+        }
+
+    }
+
+    // Replace any messages with local ones (if given)
+    if (file) {
+        readLocalMessageFile(file);
+    }
+}
+
+
+/// Read local message file
+void
+LoggerManager::readLocalMessageFile(const char* file) {
+
+    Logger logger("log");
+
+    MessageDictionary& dictionary = MessageDictionary::globalDictionary();
+    MessageReader reader(&dictionary);
+    try {
+        logger.info(MSG_RDLOCMES).arg(file);
+        reader.readFile(file, MessageReader::REPLACE);
+
+        // File successfully read, list the duplicates
+        MessageReader::MessageIDCollection unknown = reader.getNotAdded();
+        for (MessageReader::MessageIDCollection::const_iterator
+            i = unknown.begin(); i != unknown.end(); ++i) {
+            string message_id = boost::lexical_cast<string>(*i);
+                logger.warn(MSG_IDNOTFND).arg(message_id);
+        }
+    }
+    catch (MessageException& e) {
+        MessageID ident = e.id();
+        vector<string> args = e.arguments();
+        switch (args.size()) {
+        case 0:
+            LOG_ERROR(logger, ident);
+            break;
+
+        case 1:
+            LOG_ERROR(logger, ident).arg(args[0]);
+            break;
+
+        case 2:
+            LOG_ERROR(logger, ident).arg(args[0]).arg(args[1]);
+            break;
+
+        default:    // 3 or more (3 should be the maximum)
+            LOG_ERROR(logger, ident).arg(args[0]).arg(args[1]).arg(args[2]);
+        }
+    }
+}
+
 } // namespace log
 } // namespace isc
diff --git a/src/lib/log/logger_manager.h b/src/lib/log/logger_manager.h
index 4382ebe..f36e493 100644
--- a/src/lib/log/logger_manager.h
+++ b/src/lib/log/logger_manager.h
@@ -56,6 +56,32 @@ public:
         processEnd();
     }
 
+    /// \brief Process a single specification
+    ///
+    /// A convenience function for a single specification.
+    ///
+    /// \param spec Specification to process
+    void process(const LoggerSpecification& spec) {
+        processInit();
+        processSpecification(spec);
+        processEnd();
+    }
+
+    /// \brief Run-Time Initialization
+    ///
+    /// Performs run-time initialization of the logger system, in particular
+    /// supplying the root logger name and name of a replacement message file.
+    ///
+    /// This must be the first logging function called in the program.
+    ///
+    /// \param root Name of the root logger
+    /// \param file Name of the local message file.
+    /// \param severity Severity at which to log
+    /// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
+    static void init(const std::string& root, const char* file = NULL,
+                    isc::log::Severity severity = isc::log::INFO,
+                    int dbglevel = 0);
+
 private:
     /// \brief Initialize Processing
     ///
@@ -70,13 +96,21 @@ private:
     /// either the logger does not exist or has been made inactive.
     void processSpecification(const LoggerSpecification& spec);
 
-
     /// \brief End Processing
     ///
     /// Place holder for finish processing.
     /// TODO: Check that the root logger has something enabled
     void processEnd();
 
+    /// \brief Read local message file
+    ///
+    /// Reads the local message file into the global dictionary, overwriting
+    /// existing messages.  If the file contained any message IDs not in the
+    /// dictionary, they are listed in a warning message.
+    ///
+    /// \param file Name of the local message file
+    static void readLocalMessageFile(const char* file);
+
     // Members
     LoggerManagerImpl*  impl_;      ///< Pointer to implementation
 };
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
new file mode 100644
index 0000000..29e9339
--- /dev/null
+++ b/src/lib/log/logger_manager_impl.cc
@@ -0,0 +1,176 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+
+#include <log4cplus/logger.h>
+#include <log4cplus/configurator.h>
+#include <log4cplus/consoleappender.h>
+
+#include "log/logger_level_impl.h"
+#include "log/logger_manager_impl.h"
+#include "log/logger_specification.h"
+#include "log/root_logger_name.h"
+
+#include "log/logger.h"
+#include "log/messagedef.h"
+
+#include "exceptions/exceptions.h"
+
+// Generated exceptions.  Methods in this file can't log exceptions as they may
+// occur when logging is disabled or in an inconsistent state.
+class UnknownLoggingDestination : public isc::Exception {
+public:
+    UnknownLoggingDestination(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+// 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.)
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Reset hierarchy back to default.  Note that this does not delete existing
+// loggers, it makes them inactive.  (So a logger is never removed, even if a
+// configuration update removes the logger.)
+
+void
+LoggerManagerImpl::processInit() {
+    log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
+    initRootLogger();
+}
+
+// Process logging specification.  Set up the common states then dispatch to
+// add output specifications.
+
+void
+LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
+
+    // Get/construct the logger for which this specification applies.
+    log4cplus::Logger logger = (spec.getName() == getRootLoggerName()) ?
+                               log4cplus::Logger::getRoot() :
+                               log4cplus::Logger::getInstance(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) {
+
+        // Yes, so 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:
+                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));
+    setConsoleAppenderLayout(console);
+    logger.addAppender(console);
+}
+
+
+// One-time initialization of the log4cplus system
+
+void
+LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
+
+    // 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();
+
+    // And initialize the root logger
+    initRootLogger(severity, dbglevel);
+}
+
+// Initialize the root logger
+void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
+                                       int dbglevel) {
+
+    // Set the severity for the root logger
+    log4cplus::Logger::getRoot().setLogLevel(
+            LoggerLevelImpl::convertFromBindLevel(Level(severity, dbglevel)));
+
+    // Retrieve the appenders on the root instance and set the layout to
+    // use the "console" pattern.
+    log4cplus::SharedAppenderPtrList list =
+        log4cplus::Logger::getRoot().getAllAppenders();
+    for (log4cplus::SharedAppenderPtrList::iterator i = list.begin();
+         i != list.end(); ++i) {
+         setConsoleAppenderLayout(*i);
+    }
+}
+
+// Set the the "console" layout for the given appenders.  This layout includes
+// a date/time and the name of the logger.
+
+void LoggerManagerImpl::setConsoleAppenderLayout(
+        log4cplus::SharedAppenderPtr& appender)
+{
+    // Create the pattern we want for the output - local time.
+    string pattern = "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [";
+    pattern += getRootLoggerName() + string(".%c] %m\n");
+
+    // Finally the text of the message
+    auto_ptr<log4cplus::Layout> layout(new log4cplus::PatternLayout(pattern));
+    appender->setLayout(layout);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_manager_impl.h b/src/lib/log/logger_manager_impl.h
index 560d785..6dc488c 100644
--- a/src/lib/log/logger_manager_impl.h
+++ b/src/lib/log/logger_manager_impl.h
@@ -15,11 +15,24 @@
 #ifndef __LOGGER_MANAGER_IMPL_H
 #define __LOGGER_MANAGER_IMPL_H
 
-#include <log/logger_specification.h>
+#include <string>
+
+#include <log4cplus/appender.h>
+#include <log/logger_level.h>
+
+// Forward declaration to avoid need to include log4cplus header file here.
+namespace log4cplus {
+class Logger;
+class Appender;
+}
 
 namespace isc {
 namespace log {
 
+// Forward declarations
+class LoggerSpecification;
+class OutputOption;
+
 /// \brief Logger Manager Implementation
 ///
 /// This is the implementation of the logger manager for the log4cplus
@@ -41,6 +54,8 @@ public:
     /// This resets the hierachy of loggers back to their defaults.  This means
     /// that all non-root loggers (if they exist) are set to NOT_SET, and the
     /// root logger reset to logging informational messages.
+    ///
+    /// \param root_name BIND 10 name of the root logger
     void processInit();
 
     /// \brief Process Specification
@@ -54,6 +69,70 @@ public:
     ///
     /// Terminates the processing of the logging specifications.
     void processEnd();
+
+    /// \brief Implementation-specific initialization
+    ///
+    /// Performs any implementation-specific initialization.  It is assumed
+    /// that the name of the BIND 10 root logger can be obtained from the
+    /// global function getRootLoggerName().
+    ///
+    /// \param severity Severity to be associated with this logger
+    /// \param dbglevel Debug level associated with the root logger
+    static void init(isc::log::Severity severity = isc::log::INFO,
+                     int dbglevel = 0);
+
+private:
+    /// \brief Create console appender
+    ///
+    /// Creates an object that, when attached to a logger, will log to one
+    /// of the output streams (stdout or stderr).
+    ///
+    /// \param logger Log4cplus logger to which the appender must be attached.
+    /// \param opt Output options for this appender.
+    void createConsoleAppender(log4cplus::Logger& logger,
+                               const OutputOption& opt);
+
+    /// \brief Create file appender
+    ///
+    /// Creates an object that, when attached to a logger, will log to a
+    /// specified file.  This also includes the ability to "roll" files when
+    /// they reach a specified size.
+    ///
+    /// \param logger Log4cplus logger to which the appender must be attached.
+    /// \param opt Output options for this appender.
+    void createFileAppender(log4cplus::Logger& logger,
+                            const OutputOption& opt) {}
+
+    /// \brief Create syslog appender
+    ///
+    /// Creates an object that, when attached to a logger, will log to the
+    /// syslog file.
+    ///
+    /// \param logger Log4cplus logger to which the appender must be attached.
+    /// \param opt Output options for this appender.
+    void createSyslogAppender(log4cplus::Logger& logger,
+                              const OutputOption& opt) {}
+
+    /// \brief Set default layout and severity for root logger
+    ///
+    /// Initializes the root logger to BIND 10 defaults - console output and
+    /// the passed severity/debug level.
+    ///
+    /// \param severity Severity of messages that the logger should output.
+    /// \param dbglevel Debug level if severity = DEBUG
+    static void initRootLogger(isc::log::Severity severity = isc::log::INFO,
+                               int dbglevel = 0);
+
+    /// \brief Set layout for console appender
+    ///
+    /// Sets the layout of the specified appender to one suitable for file
+    /// or console output:
+    ///
+    /// YYYY-MM-DD HH:MM:SS.ssss <severity> [root.logger] message
+    ///
+    /// \param appender Appender for which this pattern is to be set.
+    /// \param root_name Name of the BIND 10 root logger.
+    static void setConsoleAppenderLayout(log4cplus::SharedAppenderPtr& appender);
 };
 
 } // namespace log
diff --git a/src/lib/log/logger_specification.h b/src/lib/log/logger_specification.h
index b057583..35c879c 100644
--- a/src/lib/log/logger_specification.h
+++ b/src/lib/log/logger_specification.h
@@ -53,33 +53,33 @@ public:
         additive_(additive)
     {}
 
-    /// \brief Set the name
+    /// \brief Set the name of the logger.
     ///
-    /// \param name Name of the logger .
+    /// \param name Name of the logger.
     void setName(const std::string& name) {
         name_ = name;
     }
 
-    /// \return Return logger name
+    /// \return Return logger name.
     std::string getName() const {
         return name_;
     }
 
-    /// \brief Set the severity
+    /// \brief Set the severity.
     ///
-    /// \param severity New severity of the logger .
+    /// \param severity New severity of the logger.
     void setSeverity(isc::log::Severity severity) {
         severity_ = severity;
     }
 
-    /// \return Return logger severity
+    /// \return Return logger severity.
     isc::log::Severity getSeverity() const {
         return severity_;
     }
 
-    /// \brief Set the debug level
+    /// \brief Set the debug level.
     ///
-    /// \param dbglevel New debug level of the logger .
+    /// \param dbglevel New debug level of the logger.
     void setDbglevel(int dbglevel) {
         dbglevel_ = dbglevel;
     }
@@ -89,51 +89,51 @@ public:
         return dbglevel_;
     }
 
-    /// \brief Set the additive flag
+    /// \brief Set the additive flag.
     ///
-    /// \param additive New value of the additive flag
+    /// \param additive New value of the additive flag.
     void setAdditive(bool additive) {
         additive_ = additive;
     }
 
-    /// \return Return additive flag
+    /// \return Return additive flag.
     int getAdditive() const {
         return additive_;
     }
 
-    /// \brief Add output option
+    /// \brief Add output option.
     ///
-    /// \param Option to add to the list
+    /// \param Option to add to the list.
     void addOutputOption(const OutputOption& option) {
         options_.push_back(option);
     }
 
-    /// \return Iterator to start of output options
+    /// \return Iterator to start of output options.
     iterator begin() {
         return options_.begin();
     }
 
-    /// \return Iterator to start of output options
+    /// \return Iterator to start of output options.
     const_iterator begin() const {
         return options_.begin();
     }
 
-    /// \return Iterator to end of output options
+    /// \return Iterator to end of output options.
     iterator end() {
         return options_.end();
     }
 
-    /// \return Iterator to end of output options
+    /// \return Iterator to end of output options.
     const_iterator end() const {
         return options_.end();
     }
 
-    /// \return Number of output specification options
+    /// \return Number of output specification options.
     size_t optionCount() const {
         return options_.size();
     }
 
-    /// \brief Reset back to defaults
+    /// \brief Reset back to defaults.
     void reset() {
         name_ = "";
         severity_ = isc::log::INFO;
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index 701cfef..fd88874 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -28,18 +28,10 @@
 #include <algorithm>
 #include <iostream>
 #include <string>
-#include <vector>
-#include <boost/lexical_cast.hpp>
 
 #include <log/logger.h>
+#include <log/logger_manager.h>
 #include <log/logger_support.h>
-#include <log/messagedef.h>
-#include <log/message_dictionary.h>
-#include <log/message_exception.h>
-#include <log/message_initializer.h>
-#include <log/message_reader.h>
-#include <log/message_types.h>
-#include <log/root_logger_name.h>
 
 namespace isc {
 namespace log {
@@ -50,88 +42,12 @@ using namespace std;
 // root logger and is used in all functions in this file.
 Logger logger("log");
 
-
-/// \brief Reads Local Message File
-///
-/// Reads the local message file into the global dictionary, overwriting
-/// existing messages.  If the file contained any message IDs not in the
-/// dictionary, they are listed in a warning message.
-///
-/// \param file Name of the local message file
-static void
-readLocalMessageFile(const char* file) {
-
-    MessageDictionary& dictionary = MessageDictionary::globalDictionary();
-    MessageReader reader(&dictionary);
-    try {
-        logger.info(MSG_RDLOCMES).arg(file);
-        reader.readFile(file, MessageReader::REPLACE);
-
-        // File successfully read, list the duplicates
-        MessageReader::MessageIDCollection unknown = reader.getNotAdded();
-        for (MessageReader::MessageIDCollection::const_iterator
-            i = unknown.begin(); i != unknown.end(); ++i) {
-            string message_id = boost::lexical_cast<string>(*i);
-                logger.warn(MSG_IDNOTFND).arg(message_id);
-        }
-    }
-    catch (MessageException& e) {
-        MessageID ident = e.id();
-        vector<string> args = e.arguments();
-        switch (args.size()) {
-        case 0:
-            logger.error(ident);
-            break;
-
-        case 1:
-            logger.error(ident).arg(args[0]);
-            break;
-
-        case 2:
-            logger.error(ident).arg(args[0]).arg(args[1]);
-            break;
-
-        default:    // 3 or more (3 should be the maximum)
-            logger.error(ident).arg(args[0]).arg(args[1]).arg(args[2]);
-        }
-    }
-}
-
 /// Logger Run-Time Initialization
 
 void
 initLogger(const string& root, isc::log::Severity severity, int dbglevel,
     const char* file) {
-
-    // Create the application root logger and set the default severity and
-    // debug level.  This is the logger that has the name of the application.
-    // All other loggers created in this application will be its children.
-    setRootLoggerName(root);
-    Logger root_logger(isc::log::getRootLoggerName());
-
-    // Set the severity associated with it.  If no other logger has a severity,
-    // this will be the default.
-    root_logger.setSeverity(severity, dbglevel);
-
-    // Check if there were any duplicate message IDs in the default dictionary
-    // and if so, log them.  Log using the logging facility root logger.
-    vector<string>& duplicates = MessageInitializer::getDuplicates();
-    if (!duplicates.empty()) {
-
-        // There are - sort and remove any duplicates.
-        sort(duplicates.begin(), duplicates.end());
-        vector<string>::iterator new_end =
-            unique(duplicates.begin(), duplicates.end());
-        for (vector<string>::iterator i = duplicates.begin(); i != new_end; ++i) {
-            logger.warn(MSG_DUPMSGID).arg(*i);
-        }
-
-    }
-
-    // Replace any messages with local ones (if given)
-    if (file) {
-        readLocalMessageFile(file);
-    }
+    LoggerManager::init(root, file, severity, dbglevel);
 }
 
 /// Logger Run-Time Initialization via Environment Variables
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index f4861b2..7d70eff 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -15,6 +15,8 @@
 #ifndef __LOGGER_SUPPORT_H
 #define __LOGGER_SUPPORT_H
 
+#include <unistd.h>
+
 #include <string>
 #include <log/logger.h>
 
@@ -36,8 +38,9 @@ namespace log {
 /// \param severity Severity at which to log
 /// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
 /// \param file Name of the local message file.
-void initLogger(const std::string& root, isc::log::Severity severity,
-    int dbglevel, const char* file);
+void initLogger(const std::string& root,
+                isc::log::Severity severity = isc::log::INFO,
+                int dbglevel = 0, const char* file = NULL);
 
 
 /// \brief Run-Time Initialization from Environment
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index ad00830..6c37f06 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -33,6 +33,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 TESTS += logger_support_test
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
index 19a0e9a..3b65c38 100644
--- a/src/lib/log/tests/logger_manager_unittest.cc
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -21,6 +21,10 @@
 
 #include <gtest/gtest.h>
 
+#include <boost/scoped_array.hpp>
+
+#include <exceptions/exceptions.h>
+
 #include <log/macros.h>
 #include <log/messagedef.h>
 #include <log/logger.h>
@@ -29,6 +33,7 @@
 #include <log/logger_specification.h>
 #include <log/output_option.h>
 
+using namespace isc;
 using namespace isc::log;
 using namespace std;
 
@@ -41,8 +46,6 @@ class DerivedLogger : public isc::log::Logger {
 public:
     DerivedLogger(std::string name) : isc::log::Logger(name)
     {}
-    virtual ~DerivedLogger()
-    {}
 
     static void reset() {
         isc::log::Logger::reset();
@@ -70,10 +73,14 @@ public:
 
     // Constructor - allocate file and create the specification object
     SpecificationForFileLogger() : spec_(), name_(""), logname_("filelogger") {
+
+        // Set the output to a temporary file.
         OutputOption option;
         option.destination = OutputOption::DEST_FILE;
-        name_ = option.filename = std::string(tmpnam(NULL));
+        option.filename = name_ = createTempFilename();
 
+        // Set target output to the file logger.  The defauls indicate
+        // INFO severity.
         spec_.setName(logname_);
         spec_.addOutputOption(option);
     }
@@ -100,6 +107,48 @@ public:
         return name_;
     }
 
+    // Create temporary filename
+    //
+    // The compiler warns against tmpnam() and suggests mkstemp instead.
+    // Unfortunately, this creates the filename and opens it.  So we need to
+    // close and delete the file before returning the name.  Also, the name
+    // is based on the template supplied and the name of the temporary
+    // directory may vary between systems.  So translate TMPDIR and if that
+    // does not exist, use /tmp.
+    //
+    // \return Temporary file name
+    std::string createTempFilename() {
+
+        // Get prefix.  Note that in all copies, strncpy does not guarantee
+        // a null-terminated string, hence the explict setting of the last
+        // character to NULL.
+        ostringstream filename;
+
+        if (getenv("TMPDIR") != NULL) {
+            filename << getenv("TMPDIR");
+        } else {
+            filename << "/tmp";
+        }
+        filename << "/bind10_logger_manager_test_XXXXXX";
+
+        cout << "*** file name before call is " << filename.str() << "\n";
+
+        // Copy into writeable storage for the call to mkstemp
+        boost::scoped_array<char> tname(new char[filename.str().size() + 1]);
+        strcpy(tname.get(), filename.str().c_str());
+
+        // Create file, close and delete it, and store the name for later.
+        int filenum = mkstemp(tname.get());
+        cout << "*** file name after call is " << tname.get() << "\n";
+        if (filenum == -1) {
+            isc_throw(Exception, "Unable to obtain unique filename");
+        }
+        close(filenum);
+        unlink(tname.get());
+
+        return (string(tname.get()));
+    }
+
 
 private:
     LoggerSpecification     spec_;      // Specification for this file logger
@@ -153,7 +202,7 @@ void checkFileContents(const std::string& filename, T start, T finish) {
 }
 
 // Check that the logger correctly creates something logging to a file.
-TEST_F(LoggerManagerTest, DISABLED_FileLogger) {
+TEST_F(LoggerManagerTest, FileLogger) {
 
     // Create a specification for the file logger and use the manager to
     // connect the "filelogger" logger to it.
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
index ab3b00e..649f4c6 100644
--- a/src/lib/log/tests/logger_support_test.cc
+++ b/src/lib/log/tests/logger_support_test.cc
@@ -20,68 +20,123 @@
 #include <unistd.h>
 #include <string.h>
 
+#include <boost/lexical_cast.hpp>
+
 #include <iostream>
+#include <string>
 
 #include <log/logger.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
 #include <log/macros.h>
-#include <log/logger_support.h>
 #include <log/root_logger_name.h>
 
 // Include a set of message definitions.
 #include <log/messagedef.h>
 
 using namespace isc::log;
+using namespace std;
+
+
+// Print usage information
+
+void usage() {
+    cout <<
+"logger_support_test [-h] [-s severity] [-d dbglevel] [-c stream] [localfile]\n"
+"\n"
+"   -h              Print this message and exit\n"
+"   -s severity     Set the severity of messages output.  'severity' is one\n"
+"                   of 'debug', 'info', 'warn', 'error', 'fatal', the default\n"
+"                   being 'info'.\n"
+"   -d dbglevel     Debug level.  Only interpreted if the severity is 'debug'\n"
+"                   this is a number between 0 and 99.\n"
+"   -c stream       Send output to the console.  'stream' is one of 'stdout'\n"
+"                   of 'stderr'.  The '-c' switch is incompatible with '-f'\n"
+"                   and '-l'\n"
+"\n"
+"If none of -c, -f or -l is given, by default, output is sent to stdout\n";
+}
 
-// Declare logger to use an example.
-Logger logger_ex("example");
 
-// The program is invoked:
-//
-// logger_support_test [-s severity] [-d level ] [local_file]
-//
-// "severity" is one of "debug", "info", "warn", "error", "fatal"
-// "level" is the debug level, a number between 0 and 99
-// "local_file" is the name of a local file.
-//
 // The program sets the attributes on the root logger and logs a set of
 // messages.  Looking at the output determines whether the program worked.
 
 int main(int argc, char** argv) {
+    const string ROOT_NAME = "alpha";
+
+    bool                c_found = false;    // Set true if "-c" found
+    bool                f_found = false;    // Set true if "-f" found
+    bool                l_found = false;    // Set true if "-l" found
+
+    const char*         localfile = NULL;   // Local message file
+    int                 option;             // For getopt() processing
 
-    isc::log::Severity  severity = isc::log::INFO;  // Default logger severity
-    int                 dbglevel = -1;              // Logger debug level
-    const char*         localfile = NULL;           // Local message file
-    int                 option;                     // For getopt() processing
-    Logger              logger_dlm("dlm");          // Another example logger
+    LoggerSpecification spec(ROOT_NAME);    // Logger specification
+    OutputOption        outopt;             // Logger output option
+
+    // Initialize loggers (to set the root name and initialize logging);
+    // We'll reset them later.
+    setRootLoggerName(ROOT_NAME);
+    Logger rootLogger(ROOT_NAME);
 
     // Parse options
-    while ((option = getopt(argc, argv, "s:d:")) != -1) {
+    while ((option = getopt(argc, argv, "hc:d:s:")) != -1) {
         switch (option) {
-            case 's':
-                if (strcmp(optarg, "debug") == 0) {
-                    severity = isc::log::DEBUG;
-                } else if (strcmp(optarg, "info") == 0) {
-                    severity = isc::log::INFO;
-                } else if (strcmp(optarg, "warn") == 0) {
-                    severity = isc::log::WARN;
-                } else if (strcmp(optarg, "error") == 0) {
-                    severity = isc::log::ERROR;
-                } else if (strcmp(optarg, "fatal") == 0) {
-                    severity = isc::log::FATAL;
-                } else {
-                    std::cout << "Unrecognised severity option: " <<
-                        optarg << "\n";
-                    exit(1);
-                }
-                break;
-
-            case 'd':
-                dbglevel = atoi(optarg);
-                break;
-
-            default:
-                std::cout << "Unrecognised option: " <<
-                    static_cast<char>(option) << "\n";
+        case 'c':
+            if (f_found || l_found) {
+                cerr << "Cannot specify -c with -f or -l\n";
+                return (1);
+            }
+
+            c_found = true;
+            outopt.destination = OutputOption::DEST_CONSOLE;
+
+            if (strcmp(optarg, "stdout") == 0) {
+                outopt.stream = OutputOption::STR_STDOUT;
+
+            } else if (strcmp(optarg, "stderr") == 0) {
+                outopt.stream = OutputOption::STR_STDERR;
+
+            } else {
+                cerr << "Unrecognised console option: " << optarg << "\n";
+                return (1);
+            }
+            break;
+
+        case 'd':
+            spec.setDbglevel(boost::lexical_cast<int>(optarg));
+            break;
+
+        case 'h':
+            usage();
+            return (0);
+
+        case 's':
+            if (strcmp(optarg, "debug") == 0) {
+                spec.setSeverity(isc::log::DEBUG);
+
+            } else if (strcmp(optarg, "info") == 0) {
+                spec.setSeverity(isc::log::INFO);
+
+            } else if (strcmp(optarg, "warn") == 0) {
+                spec.setSeverity(isc::log::WARN);
+
+            } else if (strcmp(optarg, "error") == 0) {
+                spec.setSeverity(isc::log::ERROR);
+
+            } else if (strcmp(optarg, "fatal") == 0) {
+                spec.setSeverity(isc::log::FATAL);
+
+            } else {
+                cerr << "Unrecognised severity option: " << optarg << "\n";
+                return (1);
+            }
+            break;
+
+        default:
+            std::cerr << "Unrecognised option: " <<
+                static_cast<char>(option) << "\n";
+            return (1);
         }
     }
 
@@ -90,9 +145,23 @@ int main(int argc, char** argv) {
     }
 
     // Update the logging parameters
-    initLogger("alpha", severity, dbglevel, localfile);
+    LoggerManager::init(ROOT_NAME, localfile, isc::log::INFO, 0);
+
+    // Set an output option if we have not done so already.
+    if (! (c_found || f_found || l_found)) {
+        outopt.destination = OutputOption::DEST_CONSOLE;
+        outopt.stream = OutputOption::STR_STDOUT;
+    }
+    spec.addOutputOption(outopt);
+
+    // Set the logging options for the root logger.
+    LoggerManager manager;
+    manager.process(spec);
+
 
     // Log a few messages
+    isc::log::Logger logger_dlm("dlm");
+    isc::log::Logger logger_ex("example");
     LOG_FATAL(logger_ex, MSG_WRITERR).arg("test1").arg("42");
     LOG_ERROR(logger_ex, MSG_RDLOCMES).arg("dummy/file");
     LOG_WARN(logger_dlm, MSG_READERR).arg("a.txt").arg("dummy reason");




More information about the bind10-changes mailing list