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

[master] Merge branch 'trac2445'

This commit is contained in:
Jelte Jansen
2012-12-11 10:56:33 +01:00
31 changed files with 754 additions and 81 deletions

View File

@@ -1376,6 +1376,7 @@ AC_OUTPUT([doc/version.ent
src/lib/log/tests/console_test.sh
src/lib/log/tests/destination_test.sh
src/lib/log/tests/init_logger_test.sh
src/lib/log/tests/buffer_logger_test.sh
src/lib/log/tests/local_file_test.sh
src/lib/log/tests/logger_lock_test.sh
src/lib/log/tests/severity_test.sh

View File

@@ -147,7 +147,7 @@ main(int argc, char* argv[]) {
// Initialize logging. If verbose, we'll use maximum verbosity.
isc::log::initLogger(AUTH_NAME,
(verbose ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
isc::log::MAX_DEBUG_LEVEL, NULL, true);
int ret = 0;
@@ -256,7 +256,9 @@ main(int argc, char* argv[]) {
// If we haven't registered callback for data sources, this will be just
// no-op.
config_session->removeRemoteConfig("data_sources");
if (config_session != NULL) {
config_session->removeRemoteConfig("data_sources");
}
delete xfrin_session;
delete config_session;

View File

@@ -48,7 +48,7 @@ else:
PREFIX = "@prefix@"
DATAROOTDIR = "@datarootdir@"
SPECFILE_LOCATION = "@datadir@/@PACKAGE@/bob.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
import subprocess
import signal
import re
@@ -76,7 +76,7 @@ import isc.bind10.socket_cache
import libutil_io_python
import tempfile
isc.log.init("b10-boss")
isc.log.init("b10-boss", buffer=True)
logger = isc.log.Logger("boss")
# Pending system-wide debug level definitions, the ones we
@@ -166,14 +166,14 @@ class ProcessStartError(Exception): pass
class BoB:
"""Boss of BIND class."""
def __init__(self, msgq_socket_file=None, data_path=None,
config_filename=None, clear_config=False,
verbose=False, nokill=False, setuid=None, setgid=None,
username=None, cmdctl_port=None, wait_time=10):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
The msgq_socket_file specifies the UNIX domain socket file that the
msgq process listens on. If verbose is True, then the boss reports
what it is doing.
@@ -407,7 +407,7 @@ class BoB:
logger.error(BIND10_STARTUP_UNEXPECTED_MESSAGE, msg)
except:
logger.error(BIND10_STARTUP_UNRECOGNISED_MESSAGE, msg)
return False
# The next few methods start the individual processes of BIND-10. They
@@ -486,7 +486,7 @@ class BoB:
time.sleep(1)
time_remaining = time_remaining - 1
msg, env = self.cc_session.group_recvmsg()
if not self.process_running(msg, "ConfigManager"):
raise ProcessStartError("Configuration manager process has not started")
@@ -503,7 +503,7 @@ class BoB:
process, the log_starting/log_started methods are not used.
"""
logger.info(BIND10_STARTING_CC)
self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
self.config_handler,
self.command_handler,
socket_file = self.msgq_socket_file)
@@ -700,7 +700,7 @@ class BoB:
except:
pass
# XXX: some delay probably useful... how much is uncertain
# I have changed the delay from 0.5 to 1, but sometime it's
# I have changed the delay from 0.5 to 1, but sometime it's
# still not enough.
time.sleep(1)
self.reap_children()
@@ -749,8 +749,8 @@ class BoB:
return os.waitpid(-1, os.WNOHANG)
def reap_children(self):
"""Check to see if any of our child processes have exited,
and note this for later handling.
"""Check to see if any of our child processes have exited,
and note this for later handling.
"""
while True:
try:
@@ -783,11 +783,11 @@ class BoB:
"""
Restart any dead processes:
* Returns the time when the next process is ready to be restarted.
* Returns the time when the next process is ready to be restarted.
* If the server is shutting down, returns 0.
* If there are no processes, returns None.
The values returned can be safely passed into select() as the
The values returned can be safely passed into select() as the
timeout value.
"""
@@ -1031,7 +1031,7 @@ boss_of_bind = None
def reaper(signal_number, stack_frame):
"""A child process has died (SIGCHLD received)."""
# don't do anything...
# don't do anything...
# the Python signal handler has been set up to write
# down a pipe, waking up our select() bit
pass
@@ -1198,7 +1198,7 @@ and the created lock file must be writable for that user.
except KeyError:
pass
# Next try getting information about the user, assuming user name
# Next try getting information about the user, assuming user name
# passed.
# If the information is both a valid user name and user number, we
# prefer the name because we try it second. A minor point, hopefully.

View File

@@ -27,7 +27,7 @@ import glob
import os.path
import imp
import isc.log
isc.log.init("b10-cfgmgr")
isc.log.init("b10-cfgmgr", buffer=True)
from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger
from isc.log_messages.cfgmgr_messages import *

View File

@@ -49,7 +49,7 @@ from hashlib import sha1
from isc.util import socketserver_mixin
from isc.log_messages.cmdctl_messages import *
isc.log.init("b10-cmdctl")
isc.log.init("b10-cmdctl", buffer=True)
logger = isc.log.Logger("cmdctl")
# Debug level for communication with BIND10

View File

@@ -45,7 +45,7 @@ import os.path
import signal
import socket
isc.log.init("b10-ddns")
isc.log.init("b10-ddns", buffer=True)
logger = isc.log.Logger("ddns")
TRACE_BASIC = logger.DBGLVL_TRACE_BASIC

View File

@@ -17,6 +17,7 @@
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <log/logger_support.h>
#include <log/logger_manager.h>
#include <boost/lexical_cast.hpp>
@@ -93,9 +94,10 @@ main(int argc, char* argv[]) {
}
// Initialize logging. If verbose, we'll use maximum verbosity.
// If standalone is enabled, do not buffer initial log messages
isc::log::initLogger(DHCP4_NAME,
(verbose_mode ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
LOG_INFO(dhcp4_logger, DHCP4_STARTING);
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
@@ -112,6 +114,10 @@ main(int argc, char* argv[]) {
LOG_ERROR(dhcp4_logger, DHCP4_SESSION_FAIL).arg(ex.what());
// Let's continue. It is useful to have the ability to run
// DHCP server in stand-alone mode, e.g. for testing
// We do need to make sure logging is no longer buffered
// since then it would not print until dhcp6 is stopped
isc::log::LoggerManager log_manager;
log_manager.process();
}
} else {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_STANDALONE);

View File

@@ -17,6 +17,7 @@
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
#include <log/logger_support.h>
#include <log/logger_manager.h>
#include <boost/lexical_cast.hpp>
@@ -103,9 +104,10 @@ main(int argc, char* argv[]) {
}
// Initialize logging. If verbose, we'll use maximum verbosity.
// If standalone is enabled, do not buffer initial log messages
isc::log::initLogger(DHCP6_NAME,
(verbose_mode ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
LOG_INFO(dhcp6_logger, DHCP6_STARTING);
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
@@ -119,8 +121,12 @@ main(int argc, char* argv[]) {
server.establishSession();
} catch (const std::exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_SESSION_FAIL).arg(ex.what());
// Let's continue. It is useful to have the ability to run
// Let's continue. It is useful to have the ability to run
// DHCP server in stand-alone mode, e.g. for testing
// We do need to make sure logging is no longer buffered
// since then it would not print until dhcp6 is stopped
isc::log::LoggerManager log_manager;
log_manager.process();
}
} else {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE);

View File

@@ -143,7 +143,7 @@ main(int argc, char* argv[]) {
// temporary initLogger() code. If verbose, we'll use maximum verbosity.
isc::log::initLogger(RESOLVER_NAME,
(verbose ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
isc::log::MAX_DEBUG_LEVEL, NULL, true);
// Print the starting message
string cmdline = argv[0];
@@ -177,7 +177,7 @@ main(int argc, char* argv[]) {
isc::cache::ResolverCache cache;
resolver->setCache(cache);
// TODO priming query, remove root from direct
// Fake a priming query result here (TODO2 how to flag non-expiry?)
// propagation to runningquery. And check for forwarder mode?
@@ -185,21 +185,21 @@ main(int argc, char* argv[]) {
isc::dns::Name("."),
isc::dns::RRClass::IN(),
isc::dns::RRType::NS()));
isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."),
isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."),
isc::dns::RRClass::IN(),
isc::dns::RRType::NS(),
isc::dns::RRTTL(8888)));
root_ns_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::NS(),
isc::dns::RRClass::IN(),
"l.root-servers.net."));
isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
isc::dns::RRClass::IN(),
isc::dns::RRType::A(),
isc::dns::RRTTL(8888)));
root_a_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::A(),
isc::dns::RRClass::IN(),
"199.7.83.42"));
isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
isc::dns::RRClass::IN(),
isc::dns::RRType::AAAA(),
isc::dns::RRTTL(8888)));
@@ -216,7 +216,7 @@ main(int argc, char* argv[]) {
cache.update(root_ns_rrset);
cache.update(root_a_rrset);
cache.update(root_aaaa_rrset);
DNSService dns_service(io_service, checkin, lookup, answer);
resolver->setDNSService(dns_service);
LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE_CREATED);

View File

@@ -31,7 +31,7 @@ import isc.util.process
import isc.log
from isc.log_messages.stats_messages import *
isc.log.init("b10-stats")
isc.log.init("b10-stats", buffer=True)
logger = isc.log.Logger("stats")
# Some constants for debug levels.
@@ -682,7 +682,7 @@ if __name__ == "__main__":
help="enable maximum debug logging")
(options, args) = parser.parse_args()
if options.verbose:
isc.log.init("b10-stats", "DEBUG", 99)
isc.log.init("b10-stats", "DEBUG", 99, buffer=True)
stats = Stats()
stats.start()
except OptionValueError as ove:

View File

@@ -39,7 +39,7 @@ import isc.util.process
import isc.log
from isc.log_messages.stats_httpd_messages import *
isc.log.init("b10-stats-httpd")
isc.log.init("b10-stats-httpd", buffer=True)
logger = isc.log.Logger("stats-httpd")
# Some constants for debug levels.
@@ -609,7 +609,7 @@ if __name__ == "__main__":
help="enable maximum debug logging")
(options, args) = parser.parse_args()
if options.verbose:
isc.log.init("b10-stats-httpd", "DEBUG", 99)
isc.log.init("b10-stats-httpd", "DEBUG", 99, buffer=True)
stats_httpd = StatsHttpd()
stats_httpd.start()
except OptionValueError as ove:

View File

@@ -36,7 +36,7 @@ from isc.xfrin.diff import Diff
from isc.server_common.auth_command import auth_loadzone_command
from isc.log_messages.xfrin_messages import *
isc.log.init("b10-xfrin")
isc.log.init("b10-xfrin", buffer=True)
logger = isc.log.Logger("xfrin")
# Pending system-wide debug level definitions, the ones we

View File

@@ -38,7 +38,7 @@ import isc.server_common.tsig_keyring
from isc.log_messages.xfrout_messages import *
isc.log.init("b10-xfrout")
isc.log.init("b10-xfrout", buffer=True)
logger = isc.log.Logger("xfrout")
# Pending system-wide debug level definitions, the ones we

View File

@@ -42,7 +42,7 @@ from isc.log_messages.zonemgr_messages import *
from isc.notify import notify_out
# Initialize logging for called modules.
isc.log.init("b10-zonemgr")
isc.log.init("b10-zonemgr", buffer=True)
logger = isc.log.Logger("zonemgr")
# Pending system-wide debug level definitions, the ones we

View File

@@ -31,6 +31,7 @@ libb10_log_la_SOURCES += message_initializer.cc message_initializer.h
libb10_log_la_SOURCES += message_reader.cc message_reader.h
libb10_log_la_SOURCES += message_types.h
libb10_log_la_SOURCES += output_option.cc output_option.h
libb10_log_la_SOURCES += buffer_appender_impl.cc buffer_appender_impl.h
EXTRA_DIST = README
EXTRA_DIST += logimpl_messages.mes

View File

@@ -338,7 +338,8 @@ Variant #1, Used by Production Programs
---------------------------------------
void isc::log::initLogger(const std::string& root,
isc::log::Severity severity = isc::log::INFO,
int dbglevel = 0, const char* file = NULL);
int dbglevel = 0, const char* file = NULL,
bool buffer = false);
This is the call that should be used by production programs:
@@ -359,6 +360,17 @@ file
The name of a local message file. This will be read and its definitions used
to replace the compiled-in text of the messages.
buffer
If set to true, initial log messages will be internally buffered, until the
first time a logger specification is processed. This way the program can
use logging before even processing its logging configuration. As soon as any
specification is processed (even an empty one), the buffered log messages will
be flushed according to the specification. Note that if this option is used,
the program SHOULD call one of the LoggerManager's process() calls (if you
are using the built-in logging configuration handling in ModuleCCSession,
this is automatically handled). If the program exits before this is done,
all log messages are dumped in a raw format to stdout (so that no messages
get lost).
Variant #2, Used by Unit Tests
------------------------------

View File

@@ -0,0 +1,96 @@
// Copyright (C) 2012 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 <log/buffer_appender_impl.h>
#include <log4cplus/loglevel.h>
#include <boost/scoped_ptr.hpp>
#include <cstdio>
namespace isc {
namespace log {
namespace internal {
BufferAppender::~BufferAppender() {
// If there is anything left in the buffer,
// it means no reconfig has been done, and
// we can assume the logging system was either
// never setup, or broke while doing so.
// So dump all that is left to stdout
try {
flushStdout();
} catch (...) {
// Ok if we can't even seem to dump to stdout, never mind.
}
}
void
BufferAppender::flushStdout() {
// This does not show a bit of information normal log messages
// do, so perhaps we should try and setup a new logger here
// However, as this is called from a destructor, it may not
// be a good idea; as we can't reliably know whether in what
// state the logger instance is now (or what the specific logger's
// settings were).
LogEventList::const_iterator it;
for (it = stored_.begin(); it != stored_.end(); ++it) {
const std::string level(it->first);
LogEventPtr event(it->second);
std::printf("%s [%s]: %s\n", level.c_str(),
event->getLoggerName().c_str(),
event->getMessage().c_str());
}
stored_.clear();
}
void
BufferAppender::flush() {
LogEventList stored_copy;
stored_.swap(stored_copy);
LogEventList::const_iterator it;
for (it = stored_copy.begin(); it != stored_copy.end(); ++it) {
LogEventPtr event(it->second);
log4cplus::Logger logger =
log4cplus::Logger::getInstance(event->getLoggerName());
logger.log(event->getLogLevel(), event->getMessage());
}
flushed_ = true;
}
size_t
BufferAppender::getBufferSize() const {
return (stored_.size());
}
void
BufferAppender::append(const log4cplus::spi::InternalLoggingEvent& event) {
if (flushed_) {
isc_throw(LogBufferAddAfterFlush,
"Internal log buffer has been flushed already");
}
// get a clone, and put the pointer in a shared_ptr in the list
std::auto_ptr<log4cplus::spi::InternalLoggingEvent> event_aptr =
event.clone();
// Also store the string representation of the log level, to be
// used in flushStdout if necessary
stored_.push_back(LevelAndEvent(
log4cplus::LogLevelManager().toString(event.getLogLevel()),
LogEventPtr(event_aptr.release())));
}
} // end namespace internal
} // end namespace log
} // end namespace isc

View File

@@ -0,0 +1,118 @@
// Copyright (C) 2012 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.
#ifndef LOG_BUFFER_H
#define LOG_BUFFER_H
#include <exceptions/exceptions.h>
#include <log4cplus/logger.h>
#include <log4cplus/spi/loggingevent.h>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace log {
namespace internal {
/// \brief Buffer add after flush
///
/// This exception is thrown if the log buffer's add() method
/// is called after the log buffer has been flushed; the buffer
/// is only supposed to be used once (until the first time a
/// logger specification is processed)
class LogBufferAddAfterFlush : public isc::Exception {
public:
LogBufferAddAfterFlush(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what)
{}
};
/// Convenience typedef for a pointer to a log event
typedef boost::shared_ptr<log4cplus::spi::InternalLoggingEvent> LogEventPtr;
/// Convenience typedef for a pair string/logeventptr, the string representing
/// the logger level, as returned by LogLevelManager::toString() at the
/// time of initial logging
typedef std::pair<std::string, LogEventPtr> LevelAndEvent;
/// Convenience typedef for a vector of LevelAndEvent instances
typedef std::vector<LevelAndEvent> LogEventList;
/// \brief Buffering Logger Appender
///
/// This class can be set as an Appender for log4cplus loggers,
/// and is used to store logging events; it simply keeps any
/// event that is passed to \c append(), and will replay them to the
/// logger that they were originally created for when \c flush() is
/// called.
///
/// The idea is that initially, a program may want to do some logging,
/// but does not know where to yet (for instance because it has yet to
/// read and parse its configuration). Any log messages before this time
/// would normally go to some default (say, stdout), and be lost in the
/// real logging destination. By buffering them (and flushing them once
/// the logger has been configured), these log messages are kept in a
/// consistent place, and are not lost.
///
/// Given this goal, this class has an extra check; it will raise
/// an exception if \c append() is called after flush().
///
/// If the BufferAppender instance is destroyed before being flushed,
/// it will dump any event it has left to stdout.
class BufferAppender : public log4cplus::Appender {
public:
/// \brief Constructor
///
/// Constructs a BufferAppender that buffers log evens
BufferAppender() : flushed_(false) {}
/// \brief Destructor
///
/// Any remaining events are flushed to stdout (there should
/// only be any events remaining if flush() was never called)
virtual ~BufferAppender();
/// \brief Close the appender
///
/// This class has no specialized handling for this method
virtual void close() {}
/// \brief Flush the internal buffer
///
/// Events that have been stored (after calls to \c append()
/// are replayed to the logger. Should only be called after
/// new appenders have been set to the logger.
void flush();
/// \brief Returns the number of stored logging events
///
/// Mainly useful for testing
size_t getBufferSize() const;
protected:
virtual void append(const log4cplus::spi::InternalLoggingEvent& event);
private:
/// \brief Helper for the destructor, flush events to stdout
void flushStdout();
LogEventList stored_;
bool flushed_;
};
} // end namespace internal
} // end namespace log
} // end namespace isc
#endif // LOG_BUFFER_H

View File

@@ -94,7 +94,7 @@ LoggerManager::processEnd() {
void
LoggerManager::init(const std::string& root, isc::log::Severity severity,
int dbglevel, const char* file)
int dbglevel, const char* file, bool buffer)
{
// Load in the messages declared in the program and registered by
// statically-declared MessageInitializer objects.
@@ -114,7 +114,9 @@ LoggerManager::init(const std::string& root, isc::log::Severity severity,
// Initialize the implementation logging. After this point, some basic
// logging has been set up and messages can be logged.
LoggerManagerImpl::init(severity, dbglevel);
// However, they will not appear until a logging specification has been
// processed (or the program exits), see TODO
LoggerManagerImpl::init(severity, dbglevel, buffer);
setLoggingInitialized();
// Check if there were any duplicate message IDs in the default dictionary

View File

@@ -76,6 +76,21 @@ public:
processEnd();
}
/// \brief Process 'empty' specification
///
/// This will disable any existing output options, and set
/// the logging to go to the built-in default (stdout).
/// If the logger has been initialized with buffering enabled,
/// all log messages up to now shall be printed to stdout.
///
/// This is mainly useful in scenarios where buffering is needed,
/// but it turns out there are no logging specifications to
/// handle.
void process() {
processInit();
processEnd();
}
/// \brief Run-Time Initialization
///
/// Performs run-time initialization of the logger system, in particular
@@ -91,13 +106,18 @@ public:
/// \param dbglevel Debug severity (ignored if "severity" is not "DEBUG")
/// \param file Name of the local message file. This must be NULL if there
/// is no local message file.
/// \param buffer If true, all log messages will be buffered until one of
/// the \c process() methods is called. If false, initial logging
/// shall go to the default output (i.e. stdout)
static void init(const std::string& root,
isc::log::Severity severity = isc::log::INFO,
int dbglevel = 0, const char* file = NULL);
int dbglevel = 0, const char* file = NULL,
bool buffer = false);
/// \brief Reset logging
///
/// Resets logging to whatever was set in the call to init().
/// Resets logging to whatever was set in the call to init(), expect for
/// the buffer option.
static void reset();
/// \brief Read local message file

View File

@@ -23,12 +23,14 @@
#include <log4cplus/syslogappender.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>
using namespace std;
@@ -40,19 +42,24 @@ namespace log {
// 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()));
@@ -65,8 +72,7 @@ LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
// Output options given?
if (spec.optionCount() > 0) {
// Yes, so replace all appenders for this logger.
// Replace all appenders for this logger.
logger.removeAllAppenders();
// Now process output specifications.
@@ -134,7 +140,17 @@ LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
logger.addAppender(fileapp);
}
// Syslog appender.
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)
@@ -147,10 +163,10 @@ LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
// One-time initialization of the log4cplus system
void
LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
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
@@ -161,22 +177,22 @@ LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel) {
// Add the additional debug levels
LoggerLevelImpl::init();
reset(severity, dbglevel);
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) {
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)
int dbglevel, bool buffer)
{
log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
@@ -191,14 +207,14 @@ void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
b10root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
Level(severity, dbglevel)));
// Set the BIND 10 root to use a console logger.
OutputOption opt;
createConsoleAppender(b10root, opt);
if (buffer) {
createBufferAppender(b10root);
} else {
OutputOption opt;
createConsoleAppender(b10root, opt);
}
}
// 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)
{
@@ -225,5 +241,31 @@ void LoggerManagerImpl::setSyslogAppenderLayout(
appender->setLayout(layout);
}
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());
assert(app != NULL);
app->flush();
}
}
} // namespace log
} // namespace isc

View File

@@ -51,15 +51,14 @@ class LoggerManagerImpl {
public:
/// \brief Constructor
LoggerManagerImpl()
{}
LoggerManagerImpl() {}
/// \brief Initialize Processing
///
/// 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.
static void processInit();
void processInit();
/// \brief Process Specification
///
@@ -71,8 +70,7 @@ public:
/// \brief End Processing
///
/// Terminates the processing of the logging specifications.
static void processEnd()
{}
void processEnd();
/// \brief Implementation-specific initialization
///
@@ -87,8 +85,11 @@ public:
///
/// \param severity Severity to be associated with this logger
/// \param dbglevel Debug level associated with the root logger
/// \param buffer If true, all log messages will be buffered until one of
/// the \c process() methods is called. If false, initial logging
/// shall go to the default output (i.e. stdout)
static void init(isc::log::Severity severity = isc::log::INFO,
int dbglevel = 0);
int dbglevel = 0, bool buffer = false);
/// \brief Reset logging
///
@@ -132,15 +133,27 @@ private:
static void createSyslogAppender(log4cplus::Logger& logger,
const OutputOption& opt);
/// \brief Create buffered appender
///
/// Appends an object to the logger that will store the log events sent
/// to the logger. These log messages are replayed to the logger in
/// processEnd().
///
/// \param logger Log4cplus logger to which the appender must be attached.
static void createBufferAppender(log4cplus::Logger& logger);
/// \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.
/// Initializes the root logger to BIND 10 defaults - console or buffered
/// output and the passed severity/debug level.
///
/// \param severity Severity of messages that the logger should output.
/// \param dbglevel Debug level if severity = DEBUG
/// \param buffer If true, all log messages will be buffered until one of
/// the \c process() methods is called. If false, initial logging
/// shall go to the default output (i.e. stdout)
static void initRootLogger(isc::log::Severity severity = isc::log::INFO,
int dbglevel = 0);
int dbglevel = 0, bool buffer = false);
/// \brief Set layout for console appender
///
@@ -161,6 +174,25 @@ private:
///
/// \param appender Appender for which this pattern is to be set.
static void setSyslogAppenderLayout(log4cplus::SharedAppenderPtr& appender);
/// \brief Store all buffer appenders
///
/// When processing a new specification, this method can be used
/// to keep a list of the buffer appenders; the caller can then
/// process the specification, and call \c flushBufferAppenders()
/// to flush and clear the list
void storeBufferAppenders();
/// \brief Flush the stored buffer appenders
///
/// This flushes the list of buffer appenders stored in
/// \c storeBufferAppenders(), and clears it
void flushBufferAppenders();
/// 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_;
};
} // namespace log

View File

@@ -46,8 +46,8 @@ setLoggingInitialized(bool state) {
void
initLogger(const string& root, isc::log::Severity severity, int dbglevel,
const char* file) {
LoggerManager::init(root, severity, dbglevel, file);
const char* file, bool buffer) {
LoggerManager::init(root, severity, dbglevel, file, buffer);
}
} // namespace log

View File

@@ -61,9 +61,13 @@ void setLoggingInitialized(bool state = true);
/// \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.
/// \param buffer If true, all log messages will be buffered until one of
/// the \c process() methods is called. If false, initial logging
/// shall go to the default output (i.e. stdout)
void initLogger(const std::string& root,
isc::log::Severity severity = isc::log::INFO,
int dbglevel = 0, const char* file = NULL);
int dbglevel = 0, const char* file = NULL,
bool buffer = false);
} // namespace log
} // namespace isc

View File

@@ -37,6 +37,15 @@ init_logger_test_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
init_logger_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
noinst_PROGRAMS += buffer_logger_test
buffer_logger_test_SOURCES = buffer_logger_test.cc
buffer_logger_test_CPPFLAGS = $(AM_CPPFLAGS)
buffer_logger_test_LDFLAGS = $(AM_LDFLAGS)
buffer_logger_test_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
buffer_logger_test_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
buffer_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
buffer_logger_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
noinst_PROGRAMS += logger_lock_test
logger_lock_test_SOURCES = logger_lock_test.cc
nodist_logger_lock_test_SOURCES = log_test_messages.cc log_test_messages.h
@@ -82,11 +91,13 @@ run_unittests_SOURCES += logger_specification_unittest.cc
run_unittests_SOURCES += message_dictionary_unittest.cc
run_unittests_SOURCES += message_reader_unittest.cc
run_unittests_SOURCES += output_option_unittest.cc
run_unittests_SOURCES += buffer_appender_unittest.cc
nodist_run_unittests_SOURCES = log_test_messages.cc log_test_messages.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS)
run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
run_unittests_LDADD = $(AM_LDADD)
run_unittests_LDADD += $(LOG4CPLUS_LIBS)
run_unittests_LDFLAGS = $(AM_LDFLAGS)
# logging initialization tests. These are put in separate programs to
@@ -124,6 +135,7 @@ check-local:
$(SHELL) $(abs_builddir)/console_test.sh
$(SHELL) $(abs_builddir)/destination_test.sh
$(SHELL) $(abs_builddir)/init_logger_test.sh
$(SHELL) $(abs_builddir)/buffer_logger_test.sh
$(SHELL) $(abs_builddir)/local_file_test.sh
$(SHELL) $(abs_builddir)/logger_lock_test.sh
$(SHELL) $(abs_builddir)/severity_test.sh
@@ -131,6 +143,7 @@ check-local:
noinst_SCRIPTS = console_test.sh
noinst_SCRIPTS += destination_test.sh
noinst_SCRIPTS += init_logger_test.sh
noinst_SCRIPTS += buffer_logger_test.sh
noinst_SCRIPTS += local_file_test.sh
noinst_SCRIPTS += logger_lock_test.sh
noinst_SCRIPTS += severity_test.sh

View File

@@ -0,0 +1,146 @@
// Copyright (C) 2012 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 "config.h"
#include <gtest/gtest.h>
#include <log/macros.h>
#include <log/logger_support.h>
#include <log/log_messages.h>
#include <log/buffer_appender_impl.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/logger.h>
#include <log4cplus/nullappender.h>
#include <log4cplus/spi/loggingevent.h>
using namespace isc::log;
using namespace isc::log::internal;
namespace isc {
namespace log {
/// \brief Specialized test class
///
/// In order to test append() somewhat directly, this
/// class implements one more method (addEvent)
class TestBufferAppender : public BufferAppender {
public:
void addEvent(const log4cplus::spi::InternalLoggingEvent& event) {
append(event);
}
};
class BufferAppenderTest : public ::testing::Test {
protected:
BufferAppenderTest() : buffer_appender1(new TestBufferAppender()),
appender1(buffer_appender1),
buffer_appender2(new TestBufferAppender()),
appender2(buffer_appender2),
logger(log4cplus::Logger::getInstance("buffer"))
{
logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
}
~BufferAppenderTest() {
// If any log messages are left, we don't care, get rid of them,
// by flushing them to a null appender
// Given the 'messages should not get lost' approach of the logging
// system, not flushing them to a null appender would cause them
// to be dumped to stdout as the test is destroyed, making
// unnecessarily messy test output.
log4cplus::SharedAppenderPtr null_appender(
new log4cplus::NullAppender());
logger.removeAllAppenders();
logger.addAppender(null_appender);
buffer_appender1->flush();
buffer_appender2->flush();
}
TestBufferAppender* buffer_appender1;
log4cplus::SharedAppenderPtr appender1;
TestBufferAppender* buffer_appender2;
log4cplus::SharedAppenderPtr appender2;
log4cplus::Logger logger;
};
// Test that log events are indeed stored, and that they are
// flushed to the new appenders of their logger
TEST_F(BufferAppenderTest, flush) {
ASSERT_EQ(0, buffer_appender1->getBufferSize());
ASSERT_EQ(0, buffer_appender2->getBufferSize());
// Create a Logger, log a few messages with the first appender
logger.addAppender(appender1);
LOG4CPLUS_INFO(logger, "Foo");
ASSERT_EQ(1, buffer_appender1->getBufferSize());
LOG4CPLUS_INFO(logger, "Foo");
ASSERT_EQ(2, buffer_appender1->getBufferSize());
LOG4CPLUS_INFO(logger, "Foo");
ASSERT_EQ(3, buffer_appender1->getBufferSize());
// Second buffer should still be empty
ASSERT_EQ(0, buffer_appender2->getBufferSize());
// Replace the appender by the second one, and call flush;
// this should cause all events to be moved to the second buffer
logger.removeAllAppenders();
logger.addAppender(appender2);
buffer_appender1->flush();
ASSERT_EQ(0, buffer_appender1->getBufferSize());
ASSERT_EQ(3, buffer_appender2->getBufferSize());
}
// Once flushed, logging new messages with the same buffer should fail
TEST_F(BufferAppenderTest, addAfterFlush) {
logger.addAppender(appender1);
buffer_appender1->flush();
EXPECT_THROW(LOG4CPLUS_INFO(logger, "Foo"), LogBufferAddAfterFlush);
// It should not have been added
ASSERT_EQ(0, buffer_appender1->getBufferSize());
// But logging should work again as long as a different buffer is used
logger.removeAllAppenders();
logger.addAppender(appender2);
LOG4CPLUS_INFO(logger, "Foo");
ASSERT_EQ(1, buffer_appender2->getBufferSize());
}
TEST_F(BufferAppenderTest, addDirectly) {
// A few direct calls
log4cplus::spi::InternalLoggingEvent event("buffer",
log4cplus::INFO_LOG_LEVEL,
"Bar", "file", 123);
buffer_appender1->addEvent(event);
ASSERT_EQ(1, buffer_appender1->getBufferSize());
// Do one from a smaller scope to make sure destruction doesn't harm
{
log4cplus::spi::InternalLoggingEvent event2("buffer",
log4cplus::INFO_LOG_LEVEL,
"Bar", "file", 123);
buffer_appender1->addEvent(event2);
}
ASSERT_EQ(2, buffer_appender1->getBufferSize());
// And flush them to the next
logger.removeAllAppenders();
logger.addAppender(appender2);
buffer_appender1->flush();
ASSERT_EQ(0, buffer_appender1->getBufferSize());
ASSERT_EQ(2, buffer_appender2->getBufferSize());
}
}
}

View File

@@ -0,0 +1,71 @@
// Copyright (C) 2012 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 <log/macros.h>
#include <log/logger_support.h>
#include <log/logger_manager.h>
#include <log/log_messages.h>
#include <util/interprocess_sync_null.h>
using namespace isc::log;
namespace {
void usage() {
std::cout << "Usage: buffer_logger_test [-n]" << std::endl;
}
} // end unnamed namespace
/// \brief Test InitLogger
///
/// A program used in testing the logger that initializes logging with
/// buffering enabled, so that initial log messages are not immediately
/// logged, but are not lost (they should be logged the moment process()
/// is called.
///
/// If -n is given as an argument, process() is never called. In this
/// case, upon exit, all leftover log messages should be printed to
/// stdout, but without normal logging additions (such as time and
/// logger name)
int
main(int argc, char** argv) {
bool do_process = true;
int opt;
while ((opt = getopt(argc, argv, "n")) != -1) {
switch (opt) {
case 'n':
do_process = false;
break;
default:
usage();
return (1);
}
}
// Note, level is INFO, so DEBUG should normally not show
// up. Unless process is never called (at which point it
// will end up in the dump at the end).
initLogger("buffertest", isc::log::INFO, 0, NULL, true);
Logger logger("log");
// No need for file interprocess locking in this test
logger.setInterprocessSync(new isc::util::InterprocessSyncNull("logger"));
LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info");
LOG_DEBUG(logger, 50, LOG_BAD_DESTINATION).arg("debug-50");
LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info");
// process should cause them to be logged
if (do_process) {
LoggerManager logger_manager;
logger_manager.process();
}
return (0);
}

View File

@@ -0,0 +1,61 @@
#!/bin/sh
# Copyright (C) 2012 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.
# Checks that the initLogger() call uses for unit tests respects the setting of
# the buffer value
#
testname="bufferLogger test"
echo $testname
failcount=0
tempfile=@abs_builddir@/buffer_logger_test_tempfile_$$
passfail() {
if [ $1 -eq 0 ]; then
echo " pass"
else
echo " FAIL"
failcount=`expr $failcount + $1`
fi
}
echo "1. Checking that buffer initialization works"
echo -n " - Buffer including process() call: "
cat > $tempfile << .
INFO [buffertest.log] LOG_BAD_SEVERITY unrecognized log severity: info
INFO [buffertest.log] LOG_BAD_SEVERITY unrecognized log severity: info
.
./buffer_logger_test 2>&1 | \
sed -e 's/\[\([a-z0-9\.]\{1,\}\)\/\([0-9]\{1,\}\)\]/[\1]/' | \
cut -d' ' -f3- | diff $tempfile -
passfail $?
echo -n " - Buffer excluding process() call: "
cat > $tempfile << .
INFO [buffertest.log]: LOG_BAD_SEVERITY unrecognized log severity: info
DEBUG [buffertest.log]: LOG_BAD_DESTINATION unrecognized log destination: debug-50
INFO [buffertest.log]: LOG_BAD_SEVERITY unrecognized log severity: info
.
./buffer_logger_test -n 2>&1 | diff $tempfile -
passfail $?
# Tidy up.
rm -f $tempfile
exit $failcount

View File

@@ -34,7 +34,7 @@ import bind10_config
import isc.log
from isc.log_messages.cfgmgr_messages import *
logger = isc.log.Logger("cfgmgr")
logger = isc.log.Logger("cfgmgr", buffer=True)
class ConfigManagerDataReadError(Exception):
"""This exception is thrown when there is an error while reading
@@ -224,8 +224,13 @@ class ConfigManager:
def check_logging_config(self, config):
if self.log_module_name in config:
# If there is logging config, apply it.
ccsession.default_logconfig_handler(config[self.log_module_name],
self.log_config_data)
else:
# If there is no logging config, we still need to trigger the
# handler, so make it use defaults (and flush any buffered logs)
ccsession.default_logconfig_handler({}, self.log_config_data)
def notify_boss(self):
"""Notifies the Boss module that the Config Manager is running"""
@@ -313,11 +318,11 @@ class ConfigManager:
self.config = ConfigManagerData.read_from_file(self.data_path,
self.\
database_filename)
self.check_logging_config(self.config.data);
except ConfigManagerDataEmpty:
# ok, just start with an empty config
self.config = ConfigManagerData(self.data_path,
self.database_filename)
self.check_logging_config(self.config.data);
def write_config(self):
"""Write the current configuration to the file specificied at init()"""

View File

@@ -166,17 +166,23 @@ reset(PyObject*, PyObject*) {
}
PyObject*
init(PyObject*, PyObject* args) {
init(PyObject*, PyObject* args, PyObject* arg_keywords) {
const char* root;
const char* file(NULL);
const char* severity("INFO");
bool buffer = false;
int dbglevel(0);
if (!PyArg_ParseTuple(args, "s|siz", &root, &severity, &dbglevel, &file)) {
const char* const keywords[] = { "name", "severity", "debuglevel", "file",
"buffer", NULL };
if (!PyArg_ParseTupleAndKeywords(args, arg_keywords, "s|sizb",
const_cast<char**>(keywords), &root,
&severity, &dbglevel, &file, &buffer)) {
return (NULL);
}
try {
LoggerManager::init(root, getSeverity(severity), dbglevel, file);
LoggerManager::init(root, getSeverity(severity), dbglevel, file,
buffer);
}
catch (const std::exception& e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
@@ -266,12 +272,19 @@ PyMethodDef methods[] = {
"need to call it. It returns None if the message does not exist."},
{"reset", reset, METH_NOARGS,
"Reset all logging. For testing purposes only, do not use."},
{"init", init, METH_VARARGS,
{"init", reinterpret_cast<PyCFunction>(init), METH_VARARGS | METH_KEYWORDS,
"Run-time initialization. You need to call this before you do any "
"logging, to configure the root logger name. You may also provide "
"logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
"'FATAL'), a debug level (integer in the range 0-99) and a file name "
"of a dictionary with message text translations."},
"Arguments:\n"
"name: root logger name\n"
"severity (optional): one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
"'FATAL'\n"
"debuglevel (optional): a debug level (integer in the range 0-99) "
"file (optional): a file name of a dictionary with message text "
"translations\n"
"buffer (optional), boolean, when True, causes all log messages "
"to be stored internally until log_config_update is called, at "
"which point they shall be logged."},
{"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS,
"Resets the configuration of the root logger to that set by the "
"B10_XXX environment variables. It is aimed at unit tests, where "
@@ -655,7 +668,7 @@ PyTypeObject logger_type = {
NULL, // tp_as_number
NULL, // tp_as_sequence
NULL, // tp_as_mapping
NULL, // tp_hash
NULL, // tp_hash
NULL, // tp_call
NULL, // tp_str
NULL, // tp_getattro

View File

@@ -56,6 +56,28 @@ class Manager(unittest.TestCase):
# ignore errors like missing file?
isc.log.init("root", "INFO", 0, "/no/such/file");
def test_init_keywords(self):
isc.log.init(name="root", severity="DEBUG", debuglevel=50, file=None,
buffer=True)
# unknown keyword
self.assertRaises(TypeError, isc.log.init, name="root", foo="bar")
# Replace the values for each keyword by a wrong type, one by one
self.assertRaises(TypeError, isc.log.init, name=1,
severity="DEBUG", debuglevel=50, file=None,
buffer=True)
self.assertRaises(TypeError, isc.log.init, name="root",
severity=2, debuglevel=50, file=None,
buffer=True)
self.assertRaises(TypeError, isc.log.init, name="root",
severity="DEBUG", debuglevel="50", file=None,
buffer=True)
self.assertRaises(TypeError, isc.log.init, name="root",
severity="DEBUG", debuglevel=50, file=1,
buffer=True)
self.assertRaises(TypeError, isc.log.init, name="root",
severity="DEBUG", debuglevel=50, file=None,
buffer=None)
def test_log_config_update(self):
log_spec = json.dumps(isc.config.module_spec_from_file(path_search('logging.spec', bind10_config.PLUGIN_PATHS)).get_full_spec())