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:
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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.
|
||||
|
@@ -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 *
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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:
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
------------------------------
|
||||
|
96
src/lib/log/buffer_appender_impl.cc
Normal file
96
src/lib/log/buffer_appender_impl.cc
Normal 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
|
118
src/lib/log/buffer_appender_impl.h
Normal file
118
src/lib/log/buffer_appender_impl.h
Normal 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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
146
src/lib/log/tests/buffer_appender_unittest.cc
Normal file
146
src/lib/log/tests/buffer_appender_unittest.cc
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
71
src/lib/log/tests/buffer_logger_test.cc
Normal file
71
src/lib/log/tests/buffer_logger_test.cc
Normal 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);
|
||||
}
|
61
src/lib/log/tests/buffer_logger_test.sh.in
Executable file
61
src/lib/log/tests/buffer_logger_test.sh.in
Executable 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
|
@@ -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()"""
|
||||
|
@@ -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
|
||||
|
@@ -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())
|
||||
|
||||
|
Reference in New Issue
Block a user