BIND 10 master, updated. 4d62e02fd9d410441b2f94f982e4a3b4b6e85ab4 [master] ChangeLog for trac #438
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Feb 3 18:01:44 UTC 2011
The branch, master has been updated
via 4d62e02fd9d410441b2f94f982e4a3b4b6e85ab4 (commit)
via 7b1606cea7af15dc71f5ec1d70d958b00aa98af7 (commit)
via ce77ca466280e9c63f5c47c001cd3e98381cd56a (commit)
via 18964f9374531c8f131065dcd53aefd998e080ac (commit)
via f70d352db01e774ad9561499f2205de3e66e8dbb (commit)
via 6c8cd2a184118179db9e54eed8967cedbafecbab (commit)
via a0d8f67619db8737abda16b3ef9b1b731ab1bfa1 (commit)
via 6b118748704d34fbea4d80cbed3a1094abd22e11 (commit)
via 635c6e467ed5d9bfe731a726bec673a70aad0d0a (commit)
via b4c068b2189668459f6f945d6cbe51e294af4e8d (commit)
via 3be136324e68d5ebab0b0d3da88550a920662c98 (commit)
via 758460fa8ad9cd35841f5c44dd700d9f74f3d070 (commit)
via 3666c6d78561034b5235d91547f94c554adc4b11 (commit)
via ebb344c1e2b8bb7eaa424ae4b03a46ac01787ff9 (commit)
via 17684b0c3c8dae44b22c9f69945fbd4b24faa9bf (commit)
from 76fa3d127cf0118000f368195cde54b9b49063f1 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 4d62e02fd9d410441b2f94f982e4a3b4b6e85ab4
Author: Stephen Morris <stephen at isc.org>
Date: Thu Feb 3 18:01:01 2011 +0000
[master] ChangeLog for trac #438
commit 7b1606cea7af15dc71f5ec1d70d958b00aa98af7
Merge: 76fa3d127cf0118000f368195cde54b9b49063f1 ce77ca466280e9c63f5c47c001cd3e98381cd56a
Author: Stephen Morris <stephen at isc.org>
Date: Thu Feb 3 17:53:45 2011 +0000
[master] Merge branch 'trac438'
Conflicts:
configure.ac
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 11 +-
configure.ac | 50 +++
src/lib/asiolink/tests/Makefile.am | 2 +-
src/lib/log/Makefile.am | 39 ++-
src/lib/log/compiler/Makefile.am | 20 +
src/lib/log/compiler/message.cc | 450 ++++++++++++++++++++
src/lib/log/dbglevels.h | 31 ++
src/lib/log/documentation.txt | 371 ++++++++++++++++
src/lib/log/filename.cc | 140 ++++++
src/lib/log/filename.h | 163 +++++++
src/lib/log/logger.cc | 307 +++++++++++++
src/lib/log/logger.h | 327 ++++++++++++++
src/lib/log/logger_support.cc | 116 +++++
src/lib/log/logger_support.h | 43 ++
src/lib/log/message_dictionary.cc | 116 +++++
src/lib/log/message_dictionary.h | 150 +++++++
src/lib/log/message_exception.cc | 28 ++
src/lib/log/message_exception.h | 90 ++++
src/lib/log/message_initializer.cc | 32 ++
src/lib/log/message_initializer.h | 63 +++
src/lib/log/message_reader.cc | 184 ++++++++
src/lib/log/message_reader.h | 175 ++++++++
src/lib/log/message_types.h | 32 ++
src/lib/log/messagedef.cc | 27 ++
src/lib/log/messagedef.h | 24 +
src/lib/log/messagedef.mes | 82 ++++
src/lib/log/root_logger_name.cc | 26 ++
src/lib/log/root_logger_name.h | 66 +++
src/lib/log/strutil.cc | 138 ++++++
src/lib/log/strutil.h | 147 +++++++
src/lib/log/tests/Makefile.am | 45 ++
src/lib/log/tests/filename_unittest.cc | 181 ++++++++
src/lib/log/tests/localdef.mes | 23 +
src/lib/log/tests/logger_support_test.cc | 109 +++++
src/lib/log/tests/logger_unittest.cc | 395 +++++++++++++++++
src/lib/log/tests/message_dictionary_unittest.cc | 173 ++++++++
src/lib/log/tests/message_initializer_unittest.cc | 72 ++++
.../log/tests/message_initializer_unittest_2.cc | 41 ++
src/lib/log/tests/message_reader_unittest.cc | 228 ++++++++++
src/lib/log/tests/root_logger_name_unittest.cc | 52 +++
src/lib/log/tests/run_time_init_test.sh.in | 84 ++++
src/lib/log/tests/run_unittests.cc | 23 +
src/lib/log/tests/strutil_unittest.cc | 216 ++++++++++
src/lib/log/tests/xdebuglevel_unittest.cc | 205 +++++++++
src/lib/log/xdebuglevel.cc | 148 +++++++
src/lib/log/xdebuglevel.h | 164 +++++++
46 files changed, 5605 insertions(+), 4 deletions(-)
create mode 100644 src/lib/log/compiler/Makefile.am
create mode 100644 src/lib/log/compiler/message.cc
create mode 100644 src/lib/log/dbglevels.h
create mode 100644 src/lib/log/documentation.txt
create mode 100644 src/lib/log/filename.cc
create mode 100644 src/lib/log/filename.h
create mode 100644 src/lib/log/logger.cc
create mode 100644 src/lib/log/logger.h
create mode 100644 src/lib/log/logger_support.cc
create mode 100644 src/lib/log/logger_support.h
create mode 100644 src/lib/log/message_dictionary.cc
create mode 100644 src/lib/log/message_dictionary.h
create mode 100644 src/lib/log/message_exception.cc
create mode 100644 src/lib/log/message_exception.h
create mode 100644 src/lib/log/message_initializer.cc
create mode 100644 src/lib/log/message_initializer.h
create mode 100644 src/lib/log/message_reader.cc
create mode 100644 src/lib/log/message_reader.h
create mode 100644 src/lib/log/message_types.h
create mode 100644 src/lib/log/messagedef.cc
create mode 100644 src/lib/log/messagedef.h
create mode 100644 src/lib/log/messagedef.mes
create mode 100644 src/lib/log/root_logger_name.cc
create mode 100644 src/lib/log/root_logger_name.h
create mode 100644 src/lib/log/strutil.cc
create mode 100644 src/lib/log/strutil.h
create mode 100644 src/lib/log/tests/Makefile.am
create mode 100644 src/lib/log/tests/filename_unittest.cc
create mode 100644 src/lib/log/tests/localdef.mes
create mode 100644 src/lib/log/tests/logger_support_test.cc
create mode 100644 src/lib/log/tests/logger_unittest.cc
create mode 100644 src/lib/log/tests/message_dictionary_unittest.cc
create mode 100644 src/lib/log/tests/message_initializer_unittest.cc
create mode 100644 src/lib/log/tests/message_initializer_unittest_2.cc
create mode 100644 src/lib/log/tests/message_reader_unittest.cc
create mode 100644 src/lib/log/tests/root_logger_name_unittest.cc
create mode 100755 src/lib/log/tests/run_time_init_test.sh.in
create mode 100644 src/lib/log/tests/run_unittests.cc
create mode 100644 src/lib/log/tests/strutil_unittest.cc
create mode 100644 src/lib/log/tests/xdebuglevel_unittest.cc
create mode 100644 src/lib/log/xdebuglevel.cc
create mode 100644 src/lib/log/xdebuglevel.h
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index a023f22..f0bb4c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,17 @@
+ 162. [func] stephen
+ Added C++ logging, allowing logging at different severities.
+ Code specifies the message to be logged via a symbol, and the
+ logging code picks up the message from an in-built dictionary.
+ The contents of the dictionary can be replaced at run-time by
+ locale-specific messages. A message compiler program is provided
+ to create message header files and supply the default messages.
+ (Trac #438, git 7b1606cea7af15dc71f5ec1d70d958b00aa98af7)
+
161. [func] stephen
Added ResponseScrubber class to examine response from
a server and to remove out-of-bailiwick RRsets. Also
does cross-section checks to ensure consistency.
- (Trac 496, git b9296ca023cc9e76cda48a7eeebb0119166592c5)
+ (Trac #496, git b9296ca023cc9e76cda48a7eeebb0119166592c5)
160. [func] jelte
Updated the resolver to take 3 different timeout values;
diff --git a/configure.ac b/configure.ac
index 7de33f0..714a1f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -363,6 +363,50 @@ if test "$lcov" != "no"; then
fi
AC_SUBST(USE_LCOV)
+# Configure log4cxx header and library path
+#
+# If explicitly specified, use it.
+
+AC_ARG_WITH([log4cxx],
+ AC_HELP_STRING([--with-log4cxx=PATH],
+ [specify directory where log4cxx is installed]),
+ [
+ log4cxx_include_path="${withval}/include";
+ log4cxx_library_path="${withval}/lib"
+ ])
+
+# If not specified, try some common paths. These default to
+# /usr/include and /usr/lib if not found
+
+if test -z "$with_log4cxx"; then
+ log4cxxdirs="/usr/local /usr/pkg /opt /opt/local"
+ for d in $log4cxxdirs
+ do
+ if test -d $d/include/log4cxx; then
+ log4cxx_include_path=$d/include
+ log4cxx_library_path=$d/lib
+ break
+ fi
+ done
+fi
+
+CPPFLAGS_SAVES="$CPPFLAGS"
+if test "${log4cxx_include_path}" ; then
+ LOG4CXX_INCLUDES="-I${log4cxx_include_path}"
+ CPPFLAGS="$CPPFLAGS $LOG4CXX_INCLUDES"
+fi
+AC_CHECK_HEADER([log4cxx/logger.h],, AC_MSG_ERROR([Missing log4cxx header files.]))
+CPPFLAGS="$CPPFLAGS_SAVES"
+AC_SUBST(LOG4CXX_INCLUDES)
+
+LOG4CXX_LDFLAGS="-llog4cxx";
+if test "${log4cxx_library_path}"; then
+ LOG4CXX_LDFLAGS="-L${log4cxx_library_path} -llog4cxx"
+fi
+AC_SUBST(LOG4CXX_LDFLAGS)
+
+
+
#
# Configure Boost header path
#
@@ -652,6 +696,8 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/tests/Makefile
src/lib/xfr/Makefile
src/lib/log/Makefile
+ src/lib/log/compiler/Makefile
+ src/lib/log/tests/Makefile
src/lib/resolve/Makefile
src/lib/resolve/tests/Makefile
src/lib/testutils/Makefile
@@ -713,6 +759,7 @@ AC_OUTPUT([doc/version.ent
src/lib/dns/tests/testdata/gen-wiredata.py
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
+ src/lib/log/tests/run_time_init_test.sh
], [
chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
chmod +x src/bin/xfrin/run_b10-xfrin.sh
@@ -736,6 +783,7 @@ AC_OUTPUT([doc/version.ent
chmod +x src/bin/msgq/tests/msgq_test
chmod +x src/lib/dns/gen-rdatacode.py
chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
+ chmod +x src/lib/log/tests/run_time_init_test.sh
])
AC_OUTPUT
@@ -763,6 +811,8 @@ dnl includes too
${PYTHON_LDFLAGS}
${PYTHON_LIB}
Boost: ${BOOST_INCLUDES}
+ log4cxx: ${LOG4CXX_INCLUDES}
+ ${LOG4CXX_LDFLAGS}
SQLite: $SQLITE_CFLAGS
$SQLITE_LIBS
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 3c6cd3e..e301fb2 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -20,7 +20,7 @@ run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += asiolink_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 4afb7be..47e4f81 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -1,4 +1,39 @@
-AM_CXXFLAGS = $(B10_CXXFLAGS)
+SUBDIRS = . compiler tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+
+CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = liblog.la
-liblog_la_SOURCES = dummylog.cc dummylog.h
+liblog_la_SOURCES =
+liblog_la_SOURCES += dbglevels.h
+liblog_la_SOURCES += dummylog.h dummylog.cc Message.h
+liblog_la_SOURCES += filename.h filename.cc
+liblog_la_SOURCES += logger.cc logger.h
+liblog_la_SOURCES += logger_support.cc logger_support.h
+liblog_la_SOURCES += messagedef.cc messagedef.h
+liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
+liblog_la_SOURCES += message_exception.h message_exception.cc
+liblog_la_SOURCES += message_initializer.cc message_initializer.h
+liblog_la_SOURCES += message_reader.cc message_reader.h
+liblog_la_SOURCES += message_types.h
+liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
+liblog_la_SOURCES += strutil.h strutil.cc
+liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
+
+liblog_la_LDFLAGS = $(LOG4CXX_LDFLAGS)
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+liblog_la_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+liblog_la_CXXFLAGS += -Wno-unused-parameter
+endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+liblog_la_CXXFLAGS += -Wno-error
+endif
+liblog_la_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
new file mode 100644
index 0000000..2475036
--- /dev/null
+++ b/src/lib/log/compiler/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = *.gcno *.gcda
+
+pkglibexec_PROGRAMS = message
+message_SOURCES = message.cc
+message_LDADD = $(top_builddir)/src/lib/log/liblog.la
+
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
new file mode 100644
index 0000000..50e33a6
--- /dev/null
+++ b/src/lib/log/compiler/message.cc
@@ -0,0 +1,450 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#include <cctype>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/filename.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_reader.h>
+#include <log/messagedef.h>
+#include <log/strutil.h>
+
+#include <log/logger.h>
+
+using namespace std;
+using namespace isc::log;
+
+static const char* VERSION = "1.0-0";
+
+/// \brief Message Compiler
+///
+/// \b Overview<BR>
+/// This is the program that takes as input a message file and produces:
+///
+/// \li A .h file containing message definition
+/// \li A .cc file containing code that adds the messages to the program's
+/// message disctionary at start-up time.
+///
+/// Alternatively, the program can produce a .py file that contains the
+/// message definitions.
+///
+
+/// \b Invocation<BR>
+/// The program is invoked with the command:
+///
+/// <tt>message [-p] \<message-file\></tt>
+///
+/// It reads the message file and writes out two files of the same name but with
+/// extensions of .h and .cc.
+///
+/// If \c -p is specified, the C++ files are not written; instead a Python file
+/// of the same name (but with the file extension .py) is written.
+
+
+/// \brief Print Version
+///
+/// Prints the program's version number.
+
+static void version() {
+ cout << VERSION << "\n";
+}
+
+/// \brief Print Usage
+///
+/// Prints program usage to stdout.
+
+static void usage() {
+ cout <<
+ "Usage: message [-h] [-p] [-v] <message-file>\n" <<
+ "\n" <<
+ "-h Print this message and exit\n" <<
+ "-p Output a Python module holding the message definitions.\n" <<
+ " By default a C++ header file and implementation file are\n" <<
+
+
+ " written.\n" <<
+ "-v Print the program version and exit\n" <<
+ "\n" <<
+ "<message-file> is the name of the input message file.\n";
+}
+
+
+/// \brief Create Time
+///
+/// Returns the current time as a suitably-formatted string.
+///
+/// \return Current time
+
+static string currentTime() {
+
+ // Get the current time.
+ time_t curtime;
+ time(&curtime);
+
+ // Format it
+ char buffer[32];
+ ctime_r(&curtime, buffer);
+
+ // Convert to string and strip out the trailing newline
+ string current_time = buffer;
+ return isc::strutil::trim(current_time);
+}
+
+
+
+
+/// \brief Create Header Sentinel
+///
+/// Given the name of a file, create an #ifdef sentinel name. The name is
+/// __<name>_<ext>, where <name> is the name of the file, and <ext> is the
+/// extension less the leading period. The sentinel will be upper-case.
+///
+/// \param file Filename object representing the file.
+///
+/// \return Sentinel name
+
+static string sentinel(Filename& file) {
+
+ string name = file.name();
+ string ext = file.extension();
+ string sentinel_text = "__" + name + "_" + ext.substr(1);
+ isc::strutil::uppercase(sentinel_text);
+ return sentinel_text;
+}
+
+
+/// \brief Quote String
+///
+/// Inserts an escape character (a backslash) prior to any double quote
+/// characters. This is used to handle the fact that the input file does not
+/// contain quotes, yet the string will be included in a C++ literal string.
+
+string quoteString(const string& instring) {
+
+ // Create the output string and reserve the space needed to hold the input
+ // string. (Most input strings will not contain quotes, so this single
+ // reservation should be all that is needed.)
+ string outstring;
+ outstring.reserve(instring.size());
+
+ // Iterate through the input string, preceding quotes with a slash.
+ for (size_t i = 0; i < instring.size(); ++i) {
+ if (instring[i] == '"') {
+ outstring += '\\';
+ }
+ outstring += instring[i];
+ }
+
+ return outstring;
+}
+
+
+/// \brief Sorted Identifiers
+///
+/// Given a dictionary, return a vector holding the message IDs in sorted
+/// order.
+///
+/// \param dictionary Dictionary to examine
+///
+/// \return Sorted list of message IDs
+
+vector<MessageID> sortedIdentifiers(MessageDictionary* dictionary) {
+ vector<MessageID> ident;
+
+ for (MessageDictionary::const_iterator i = dictionary->begin();
+ i != dictionary->end(); ++i) {
+ ident.push_back(i->first);
+ }
+ sort(ident.begin(), ident.end());
+
+ return ident;
+}
+
+
+/// \brief Write Header File
+///
+/// Writes the C++ header file containing the symbol definitions.
+///
+/// \param file Name of the message file. The header file is written to a
+/// file of the same name but with a .h suffix.
+/// \param prefix Prefix string to use in symbols
+/// \param dictionary Dictionary holding the message definitions.
+
+void writeHeaderFile(const string& file, const string& prefix,
+ MessageDictionary* dictionary)
+{
+ Filename message_file(file);
+ Filename header_file(message_file.useAsDefault(".h"));
+
+ // Text to use as the sentinels.
+ string sentinel_text = sentinel(header_file);
+
+ // Open the output file for writing
+ ofstream hfile(header_file.fullName().c_str());
+
+ try {
+ if (hfile.fail()) {
+ throw MessageException(MSG_OPENOUT, header_file.fullName(),
+ strerror(errno));
+ }
+
+ // Write the header preamble. If there is an error, we'll pick it up
+ // after the last write.
+
+ hfile <<
+ "// File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "#ifndef " << sentinel_text << "\n" <<
+ "#define " << sentinel_text << "\n" <<
+ "\n" <<
+ "#include <log/message_types.h>\n" <<
+ "\n" <<
+ "namespace {\n" <<
+ "\n";
+
+ vector<MessageID> idents = sortedIdentifiers(dictionary);
+ for (vector<MessageID>::const_iterator j = idents.begin();
+ j != idents.end(); ++j) {
+ hfile << "isc::log::MessageID " << prefix << *j <<
+ " = \"" << *j << "\";\n";
+ }
+
+ // ... and finally the postamble
+ hfile <<
+ "\n" <<
+ "} // Anonymous namespace\n" <<
+ "\n" <<
+ "#endif // " << sentinel_text << "\n";
+
+ // Report errors (if any) and exit
+ if (hfile.fail()) {
+ throw MessageException(MSG_WRITERR, header_file.fullName(),
+ strerror(errno));
+ }
+
+ hfile.close();
+ }
+ catch (MessageException&) {
+ hfile.close();
+ throw;
+ }
+}
+
+
+/// \brief Convert Non Alpha-Numeric Characters to Underscores
+///
+/// Simple function for use in a call to transform
+
+char replaceNonAlphaNum(char c) {
+ return (isalnum(c) ? c : '_');
+}
+
+
+/// \brief Write Program File
+///
+/// Writes the C++ source code file. This defines an external objects whose
+/// constructor is run at initialization time. The constructor adds the message
+/// definitions to the main global dictionary.
+
+void writeProgramFile(const string& file, MessageDictionary* dictionary)
+{
+ Filename message_file(file);
+ Filename program_file(message_file.useAsDefault(".cc"));
+
+ // Open the output file for writing
+ ofstream ccfile(program_file.fullName().c_str());
+ try {
+ if (ccfile.fail()) {
+ throw MessageException(MSG_OPENOUT, program_file.fullName(),
+ strerror(errno));
+ }
+
+ // Write the preamble. If there is an error, we'll pick it up after
+ // the last write.
+
+ ccfile <<
+ "// File created from " << message_file.fullName() << " on " <<
+ currentTime() << "\n" <<
+ "\n" <<
+ "#include <cstddef>\n" <<
+ "#include <log/message_initializer.h>\n" <<
+ "\n" <<
+ "using namespace isc::log;\n" <<
+ "\n" <<
+ "namespace {\n" <<
+ "\n" <<
+ "const char* values[] = {\n";
+
+ // Output the identifiers and the associated text.
+ vector<MessageID> idents = sortedIdentifiers(dictionary);
+ for (vector<MessageID>::const_iterator i = idents.begin();
+ i != idents.end(); ++i) {
+ ccfile << " \"" << *i << "\", \"" <<
+ quoteString(dictionary->getText(*i)) << "\",\n";
+ }
+
+ // ... and the postamble
+ ccfile <<
+ " NULL\n" <<
+ "};\n" <<
+ "\n" <<
+ "} // Anonymous namespace\n" <<
+ "\n";
+
+ // Now construct a unique name. We don't put the message initializer as
+ // a static variable or in an anonymous namespace lest the C++
+ // compiler's optimizer decides it can optimise it away.
+ string unique_name = program_file.name() + program_file.extension() +
+ "_" + currentTime();
+ transform(unique_name.begin(), unique_name.end(), unique_name.begin(),
+ replaceNonAlphaNum);
+
+ // ... and write the initialization code
+ ccfile <<
+ "MessageInitializer " << unique_name << "(values);\n";
+
+ // Report errors (if any) and exit
+ if (ccfile.fail()) {
+ throw MessageException(MSG_WRITERR, program_file.fullName(),
+ strerror(errno));
+ }
+
+ ccfile.close();
+ }
+ catch (MessageException&) {
+ ccfile.close();
+ throw;
+ }
+}
+
+
+/// \brief Warn of Duplicate Entries
+///
+/// If the input file contained duplicate message IDs, only the first will be
+/// processed. However, we should warn about it.
+///
+/// \param reader Message Reader used to read the file
+
+static void warnDuplicates(MessageReader& reader) {
+
+ // Get the duplicates (the overflow) and, if present, sort them into some
+ // order and remove those which occur more than once (which mean that they
+ // occur more than twice in the input file).
+ MessageReader::MessageIDCollection duplicates = reader.getNotAdded();
+ if (duplicates.size() > 0) {
+ cout << "Warning: the following duplicate IDs were found:\n";
+
+ sort(duplicates.begin(), duplicates.end());
+ MessageReader::MessageIDCollection::iterator new_end =
+ unique(duplicates.begin(), duplicates.end());
+ for (MessageReader::MessageIDCollection::iterator i = duplicates.begin();
+ i != new_end; ++i) {
+ cout << " " << *i << "\n";
+ }
+ }
+}
+
+
+/// \brief Main Program
+///
+/// Parses the options then dispatches to the appropriate function. See the
+/// main file header for the invocation.
+
+int main(int argc, char** argv) {
+
+ const struct option loptions[] = { // Long options
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {NULL, 0, NULL, 0 }
+ };
+ const char* soptions = "hv"; // Short options
+
+ optind = 1; // Ensure we start a new scan
+ int opt; // Value of the option
+
+ while ((opt = getopt_long(argc, argv, soptions, loptions, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ usage();
+ return 0;
+
+ case 'v':
+ version();
+ return 0;
+
+ default:
+ // A message will have already been output about the error.
+ return 1;
+ }
+ }
+
+ // Do we have the message file?
+ if (optind < (argc - 1)) {
+ cout << "Error: excess arguments in command line\n";
+ usage();
+ return 1;
+ } else if (optind >= argc) {
+ cout << "Error: missing message file\n";
+ usage();
+ return 1;
+ }
+ string message_file = argv[optind];
+
+ try {
+ // Have identified the file, so process it. First create a local
+ // dictionary into which the data will be put.
+ MessageDictionary dictionary;
+
+ // Read the data into it.
+ MessageReader reader(&dictionary);
+ reader.readFile(message_file);
+
+ // Now write the header file.
+ writeHeaderFile(message_file, reader.getPrefix(), &dictionary);
+
+ // ... and the message text file.
+ writeProgramFile(message_file, &dictionary);
+
+ // Finally, warn of any duplicates encountered.
+ warnDuplicates(reader);
+ }
+ catch (MessageException& e) {
+ // Create an error message from the ID and the text
+ MessageDictionary* global = MessageDictionary::globalDictionary();
+ string text = e.id() + ", " + global->getText(e.id());
+
+ // Format with arguments
+ text = isc::strutil::format(text, e.arguments());
+ cerr << text << "\n";
+
+ return 1;
+ }
+
+ return 0;
+
+}
diff --git a/src/lib/log/dbglevels.h b/src/lib/log/dbglevels.h
new file mode 100644
index 0000000..35c6878
--- /dev/null
+++ b/src/lib/log/dbglevels.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __DBGLEVELS_H
+#define __DBGLEVELS_H
+
+/// \brief Defines Debug Levels
+///
+/// Defines the maximum and minimum debug levels and the number of levels.
+/// These are defined using #define as they are referenced in the construction
+/// of variables declared outside execution units. (In this way we avoid the
+/// "static initialization fiasco" problem.)
+
+#define MIN_DEBUG_LEVEL (0)
+#define MAX_DEBUG_LEVEL (99)
+#define NUM_DEBUG_LEVEL (MAX_DEBUG_LEVEL - MIN_DEBUG_LEVEL + 1)
+
+#endif // __DBGLEVELS_H
diff --git a/src/lib/log/documentation.txt b/src/lib/log/documentation.txt
new file mode 100644
index 0000000..1439ad5
--- /dev/null
+++ b/src/lib/log/documentation.txt
@@ -0,0 +1,371 @@
+This directory holds the first release of the logging system.
+
+Basic Ideas
+===========
+The BIND-10 logging system merges two ideas:
+
+* A hierarchical logging system similar to that used in Java (i.e. log4j)
+* Separation of message definitions and text
+
+
+Hierarchical Logging System
+===========================
+When a program writes a message to the logging system, it does so using an
+instance of the Logger class. As well as performing the write of the message,
+the logger identifies the source of the message: different sources can write
+to different destinations and can log different severities of messages. For
+example, the "cache" logger could write messages of DEBUG severity or above
+to a file while all other components write messages of "INFO" severity or above
+to the Syslog file.
+
+The loggers are hierarchical in that each logger is the child of another logger.
+The top of the hierarchy is the root logger, which does not have a parent. The
+point of the hierarchy is that unless a logger is explicitly assigned an
+attribute (such as severity of message being logger), it picks it up from the
+parent. (In BIND-10, there is the root logger (named after the program) and
+every other logger is a child of that.) So in the example above, the
+INFO/Syslog attributes could be associated with the root logger while the
+DEBUG/file attributes are associated with the "cache" logger.
+
+
+Separation of Messages Definitions And Text
+===========================================
+The reason for this is to allow the message text to be overridden by versions
+in a local language. To do this, each message is identified by an identifier
+e.g. "OPENIN". Within the program, this is the symbol passed to the logging
+system. The logger system uses the symbol as an index into a dictionary to
+retrieve the message associated with it (e.g. "unable to open %s for input").
+substitutes any message parameters (in this example, the string that is an
+invalid filename) and logs it to the destination.
+
+In the BIND-10 system, a set of default messages are linked into the program.
+At run-time. each program reads a message file, updating the stored definitions;
+this updated text is logged. However, to aid support, the message identifier
+so in the example above, the message finally logged would be something like:
+
+ OPENIN, unable to open a.txt for input
+
+
+Using The System
+================
+The steps in using the system are:
+
+1. Create a message file. This defines messages by an identification - a
+ mnemonic for the message, typically 6-12 characters long - and a message.
+ The file is described in more detail below.
+
+ Ideally the file should have a file type of ".msg".
+
+2. Run it through the message compiler to produce the .h and .cc files. It
+ is intended that this step be included in the build process. However, for
+ not run the compiler (found in the "compiler" subdirectory) manually. The
+ only argument is the name of the message file: it will produce as output
+ two files, having the same name as the input file but with file types of
+ ".h" and ".cc".
+
+ The compiler is built in the "compiler" subdirectory of the "src/lib/log"
+ directory.
+
+3. Include the .h file in your source code to define message symbols, and
+ make sure that the .cc file is compiled and linked into your program -
+ static initialization will add the symbols to the global dictionary.
+
+4. Declare loggers in your code and use them to log messages. This is described
+ in more detail below.
+
+5. To set the debug level and run-time message file, call runTimeInit (declared
+ in logger_support.h) in the main program unit. This is a temporary solution
+ for Year 2, and will be replaced at a later date, the information coming from
+ the configuration database.
+
+
+Message Files
+=============
+
+File Contents and Format
+------------------------
+A message file is a file containing message definitions. Typically there will
+be one message file for each component that declares message symbols. An
+example file could be:
+
+-- BEGIN --
+
+# Example message file
+# $ID:$
+
+$PREFIX TEST_
+TEST1 message %s is much too large
++ This message is a test for the general message code
+
+UNKNOWN unknown message
++ Issued when the message is unknown.
+
+-- END --
+
+Points to note:
+* Leading and trailing space are trimmed from the line. Although the above
+ exampl,e has every line starting at column 1, the lines could be indented if
+ desired.
+
+* Blank lines are ignored.
+
+* Lines starting with "#" are comments are are ignored. Comments must be on
+ a line by themselves - inline comments will be interpreted as part of the
+ text of the line.
+
+* Lines starting $ are directives. At present, the only directive recognised
+ is $PREFIX, which has one argument: the string used to prefix symbols. If
+ there is no facility directive, there is no prefix to the symbols. (Prefixes
+ are explained below.)
+
+* Lines starting + indicate an explanation for the preceding message. These
+ are intended to be processed by a separate program and used to generate an
+ error messages manual. However they are treated like comments by the message
+ compiler. As with comments, these must be on a line by themselves; if inline,
+ the text (including the leading "+") will be interpreted as part of the line.
+
+* Message lines. These comprise a symbol name and a message, which may
+ include zero or more printf-style tokens. Symbol names will be upper-cased
+ by the compiler.
+
+
+Message Compiler
+----------------
+The message compiler is a program built in the src/log/compiler directory.
+It processes the message file to produce two files:
+
+1) A C++ header file (called <message-file-name>.h) that holds lines of
+the form:
+
+ namespace {
+ isc::log::MessageID PREFIX_IDENTIFIER = "IDENTIFIER";
+ :
+ }
+
+The symbols define the keys in the global message dictionary. At present
+they are defined as std::strings, but a future implementation could redefine
+them as numeric values.
+
+The "PREFIX_" part of the symbol name is the string defined in the $PREFIX
+the argument to the directive. So "$PREFIX MSG_" would prefix the identifer
+ABC with "MSG_" to give the symbol MSG_ABC. Similarly "$PREFIX E" would
+prefix it with "E" to give the symbol EABC. If no $PREFIX is given, no
+prefix appears (so the symbol in this example would be ABC).
+
+
+2) A C++ source file (called <message-file-name>.cc) that holds the code to
+insert the symbols and messages into the map.
+
+This file declares an array of identifiers/messages in the form:
+
+ namespace {
+ const char* values[] = {
+ identifier1, text1,
+ identifier2, text2,
+ :
+ NULL
+ };
+ }
+
+(A more complex structure to group identifiers and their messages could be
+imposed, but as the array is generated by code and will be read by code,
+it is not needed.)
+
+It then declares an object that will add information to the global dictionary:
+
+ MessageInitializer <message-file-name>_<time>(values);
+
+(Declaring the object as "static" or in the anonymous namespace runs the risk
+of it being optimised away when the module is compiled with optimisation.
+But giving it a standard name would cause a clash when multiple files are
+used, hence an attempt at disambiguation.)
+
+The constructor of the MessageInitializer object retrieves the singleton
+global Dictionary object (created using standard methods to avoid the
+"static initialization fiasco") and adds each identifier and text to it.
+A check is made as each is added; if the identifier already exists, it is
+added to "overflow" vector; the vector is printed to the main logging output
+when logging is finally enabled (to indicate a programming error).
+
+
+Using the Logging
+=================
+To use the current version of the logging:
+
+1. Build message header file and source file as describe above.
+
+2. In the main module of the program, declare an instance of the
+ RootLoggerName class to define the name of the program's root logger, e.g.
+
+ #include <log/root_logger_name.h>
+
+ isc::log::RootLoggerName("b10-auth");
+
+ It should be declared outside an execution unit to allow other statically-
+ declared loggers to pick it up.
+
+2. In the code that needs to do logging, declare a logger with a given name,
+ e.g.
+
+ #include <log/logger.h>
+ :
+ isc::log::Logger logger("myname"); // "myname" can be anything
+
+ The above example assumes declaration outside a function. If declaring
+ non-statically within a function, declare it as:
+
+ isc::log::Logger logger("myname", true);
+
+ This is due to an apparent bug in the underlying log4cxx, where the deletion
+ of a statically-declared object at program termination can cause a segment
+ fault. (The destruction of internal memory structures can sometimes happen
+ out of order.) By default the Logger class creates the structures in its
+ constructor but does not delete them in the destruction. The default
+ behavious works because instead of reclaiming memory at program run-down,
+ the operating system reclaims it when the process is deleted.
+
+ Setting the second argument "true" causes the Logger's destructor to delete
+ the log4cxx structures. This does not cause a problem if the program is
+ not terminating. So use the second form when declaring an automatic
+ instance of isc::log::Logger on the stack.
+
+3. The main program unit should include a call to isc::log::runTimeInit()
+ (defined in logger_support.h) to set the logging severity, debug log level,
+ and external message file.
+
+ a) The logging severity is one of the enum defined in logger.h, i.e.
+
+ isc::log::Logger::DEBUG
+ isc::log::Logger::INFO
+ isc::log::Logger::WARN
+ isc::log::Logger::ERROR
+ isc::log::Logger::FATAL
+ isc::log::Logger::NONE
+
+ b) The debug log level is only interpreted when the severity is DEBUG and
+ is an integer raning from 0 to 99. 0 should be used for the highest-level
+ debug messages and 99 for the lowest-level (and typically more verbose)
+ messages.
+
+ c) Name of an external message file. This is the same as a standard message
+ file, although it should not include the $PREFIX directive. (A single
+ $PREFIX directive will be ignored; multiple directives will cause the
+ read of the file to fail with an error.) If a message is replaced, the
+ message should include the same printf-format directives in the same order
+ as the original message.
+
+4. Issue logging calls using methods on logger, e.g.
+
+ logger.error(DPS_NSTIMEOUT, "isc.org");
+
+ (where, in the example above we might have defined the symbol in the message
+ file with something along the lines of:
+
+ $PREFIX DPS_
+ :
+ NSTIMEOUT queries to all nameservers for %s have timed out
+
+ At present, the only logging is to the console.
+
+
+Severity Guidelines
+===================
+When using logging, the question arises, what severity should a message be
+logged at? The following is a suggestion - as always, the decision must be
+made in the context of which the message is logged.
+
+FATAL
+-----
+The program has encountered an error that is so severe that it cannot
+continue (or there is no point in continuing). When a fatal error has been
+logged, the program will usually exit immediately (via a call to abort()) or
+shortly afterwards, after dumping some diagnostic information.
+
+ERROR
+-----
+Something has happened such that the program can continue but the results
+for the current (or future) operations cannot be guaranteed to be correct,
+or the results will be correct but the service is impaired. For example,
+the program started but attempts to open one or more network interfaces failed.
+
+WARN
+----
+An unusual event happened. Although the program will continue working
+normally, the event was sufficiently out of the ordinary to warrant drawings
+attention to it. For example, at program start-up a zone was loaded that
+contained no resource records,
+
+INFO
+----
+A normal but significant event has occurred that should be recorded,
+e.g. the program has started or is just about to terminate, a new zone has
+been created, etc.
+
+DEBUG
+-----
+This severity is only enabled on for debugging purposes. A debug level is
+associated with debug messages, level 0 (the default) being for high-level
+messages and level 99 (the maximum) for the lowest level. How the messages
+are distributed between the levels is up to the developer. So if debugging
+the NSAS (for example), a level 0 message might record the creation of a new
+zone, a level 10 recording a timeout when trying to get a nameserver address,
+but a level 50 would record every query for an address. (And we might add
+level 51 to record every update of the RTT.)
+
+Note that like severities, levels are cumulative; so if level 25 is set as the
+debug level, all debug levels from 0 to 25 will be output. In fact, it is
+probably easier to visualise the debug levels as part of the severity system:
+
+ FATAL High
+ ERROR
+ WARN
+ INFO
+ DEBUG level 0
+ DEBUG level 1
+ :
+ DEBUG level 99 Low
+
+When a particular severity is set, it - and all severities and/or debug
+levels above it - will be logged.
+
+Logging Sources v Logging Severities
+------------------------------------
+When logging events, make a distinction between events related to the server
+and events related to DNS messages received. Caution needs to be exercised
+with the latter as, if the logging is enabled in the normal course of events,
+such logging could be a denoial of service vector. For example, suppose that
+the main authoritiative service logger were to log both zone loading and
+unloading as INFO and a warning message if it received an invalid packet. An
+attacker could make the INFO messages unusable by flooding the server with
+malformed packets.
+
+There are two approaches to get round this:
+
+a) Make the logging of packet-dependent events a DEBUG-severity message.
+DEBUG is not enabled by default, so these events will not be recorded unless
+DEBUG is specifically chosen.
+
+b) Record system-related and packet-related messages via different loggers
+(e.g. in the example given, sever events could be logged using the logger
+"auth" and packet-related events at that level logged using the logger
+"pkt-auth".)
+As the loggers are independent and the severity levels independent, fine-tuning
+of what and what is not recorded can be achieved.
+
+
+Outstanding Issues
+==================
+* Ability to configure system according to configuration database.
+* Update the build procedure to create .cc and .h files from the .msg file
+ during the build process. (Requires that the message compiler is built first.)
+
+
+Notes
+=====
+The message compiler is written in C++ (instead of Python) because it
+contains a component that reads the message file. This component is used
+in both the message compiler and the server; in the server it is used when
+the server starts up (or when triggered by a command) to read in a message
+file to overwrite the internal dictionary. Writing it in C++ means there
+is only one piece of code that does this functionality.
+
diff --git a/src/lib/log/filename.cc b/src/lib/log/filename.cc
new file mode 100644
index 0000000..949ed9f
--- /dev/null
+++ b/src/lib/log/filename.cc
@@ -0,0 +1,140 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <ctype.h>
+
+#include <log/filename.h>
+#include <log/strutil.h>
+
+using namespace std;
+
+
+namespace isc {
+namespace log {
+
+// Split string into components. Any backslashes are assumed to have
+// been replaced by forward slashes.
+
+void
+Filename::split(const string& full_name, string& directory,
+ string& name, string& extension) const
+{
+ directory = name = extension = "";
+ bool dir_present = false;
+ if (!full_name.empty()) {
+
+ // Find the directory.
+ size_t last_slash = full_name.find_last_of('/');
+ if (last_slash != string::npos) {
+
+ // Found the last slash, so extract directory component and
+ // set where the scan for the last_dot should terminate.
+ directory = full_name.substr(0, last_slash + 1);
+ if (last_slash == full_name.size()) {
+
+ // The entire string was a directory, so exit not and don't
+ // do any more searching.
+ return;
+ }
+
+ // Found a directory so note the fact.
+ dir_present = true;
+ }
+
+ // Now search backwards for the last ".".
+ size_t last_dot = full_name.find_last_of('.');
+ if ((last_dot == string::npos) ||
+ (dir_present && (last_dot < last_slash))) {
+
+ // Last "." either not found or it occurs to the left of the last
+ // slash if a directory was present (so it is part of a directory
+ // name). In this case, the remainder of the string after the slash
+ // is the name part.
+ name = full_name.substr(last_slash + 1);
+ return;
+ }
+
+ // Did find a valid dot, so it and everything to the right is the
+ // extension...
+ extension = full_name.substr(last_dot);
+
+ // ... and the name of the file is everything in between.
+ if ((last_dot - last_slash) > 1) {
+ name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
+ }
+ }
+
+}
+
+// Expand the stored filename with the default.
+
+string
+Filename::expandWithDefault(const string& defname) const {
+
+ string def_directory("");
+ string def_name("");
+ string def_extension("");
+
+ // Normalize the input string.
+ string copy_defname = isc::strutil::trim(defname);
+#ifdef WIN32
+ isc::strutil::normalizeSlash(copy_defname);
+#endif
+
+ // Split into the components
+ split(copy_defname, def_directory, def_name, def_extension);
+
+ // Now construct the result.
+ string retstring =
+ (directory_.empty() ? def_directory : directory_) +
+ (name_.empty() ? def_name : name_) +
+ (extension_.empty() ? def_extension : extension_);
+ return retstring;
+}
+
+// Use the stored name as default for a given name
+
+string
+Filename::useAsDefault(const string& name) const {
+
+ string name_directory("");
+ string name_name("");
+ string name_extension("");
+
+ // Normalize the input string.
+ string copy_name = isc::strutil::trim(name);
+#ifdef WIN32
+ isc::strutil::normalizeSlash(copy_name);
+#endif
+
+ // Split into the components
+ split(copy_name, name_directory, name_name, name_extension);
+
+ // Now construct the result.
+ string retstring =
+ (name_directory.empty() ? directory_ : name_directory) +
+ (name_name.empty() ? name_ : name_name) +
+ (name_extension.empty() ? extension_ : name_extension);
+ return retstring;
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/filename.h b/src/lib/log/filename.h
new file mode 100644
index 0000000..29a9cc8
--- /dev/null
+++ b/src/lib/log/filename.h
@@ -0,0 +1,163 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __FILENAME_H
+#define __FILENAME_H
+
+#include <string>
+
+#include <strutil.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Class to Manipulate Filenames
+///
+/// This is a utility class to manipulate filenames. It repeats some of the
+/// features found in the Boost filename class, but is self-contained so avoids
+/// the need to link in the Boost library.
+///
+/// A Unix-style filename comprises three parts:
+///
+/// Directory - everything up to and including the last "/". If there is no
+/// "/" in the string, there is no directory component. Note that the
+/// requirement of a trailing slash eliminates the ambiguity of whether a
+/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
+/// name of a directory or is could be a file. The interpretation here is that
+/// "beta" is the name of a file (although that file could be a directory).
+///
+/// Note: Under Windows, the drive letter is considered to be part of the
+/// directory specification. Unless this class becomes more widely-used on
+/// Windows, there is no point in adding redundant code.
+///
+/// Name - everthing from the character after the last "/" up to but not
+/// including the last ".".
+///
+/// Extension - everthing from the right-most "." (after the right-most "/") to
+/// the end of the string. If there is no "." after the last "/", there is
+/// no file extension.
+///
+/// (Note that on Windows, this function will replace all "\" characters
+/// with "/" characters on input strings.)
+///
+/// This class provides functions for extracting the components and for
+/// substituting components.
+
+
+class Filename {
+public:
+
+ /// \brief Constructor
+ Filename(const std::string& name) :
+ full_name_(""), directory_(""), name_(""), extension_("")
+ {
+ setName(name);
+ }
+
+ /// \brief Sets Stored Filename
+ ///
+ /// \param name New name to replaced currently stored name
+ void setName(const std::string& name) {
+ full_name_ = isc::strutil::trim(name);
+#ifdef WIN32
+ isc::strutil::normalizeSlash(full_name_);
+#endif
+ split(full_name_, directory_, name_, extension_);
+ }
+
+ /// \return Stored Filename
+ std::string fullName() const {
+ return full_name_;
+ }
+
+ /// \return Directory of Given File Name
+ std::string directory() const {
+ return directory_;
+ }
+
+ /// \return Name of Given File Name
+ std::string name() const {
+ return name_;
+ }
+
+ /// \return Extension of Given File Name
+ std::string extension() const {
+ return extension_;
+ }
+
+ /// \brief Expand Name with Default
+ ///
+ /// A default file specified is supplied and used to fill in any missing
+ /// fields. For example, if the name stored is "/a/b" and the supplied
+ /// name is "c.d", the result is "/a/b.d": the only field missing from the
+ /// stored name is the extension, which is supplied by the default.
+ /// Another example would be to store "a.b" and to supply a default of
+ /// "/c/d/" - the result is "/c/d/a.b". (Note that if the supplied default
+ /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
+ /// a directory.)
+ ///
+ /// \param defname Default name
+ ///
+ /// \return Name expanded with defname.
+ std::string expandWithDefault(const std::string& defname) const;
+
+ /// \brief Use as Default and Substitute into String
+ ///
+ /// Does essentially the inverse of expand(); that filled in the stored
+ /// name with a default and returned the result. This treats the stored
+ /// name as the default and uses it to fill in a given name. In essence,
+ /// the code:
+ /// \code
+ /// Filename f("/a/b");
+ /// result = f.expandWithdefault("c.d");
+ /// \endcode
+ /// gives as a result "/a/b.d". This is the same as:
+ /// \code
+ /// Filename f("c.d");
+ /// result = f.useAsDefault("/a/b");
+ /// \endcode
+ ///
+ /// \param name Name to expand
+ ///
+ /// \return Name expanded with stored name
+ std::string useAsDefault(const std::string&) const;
+
+private:
+ /// \brief Split Name into Components
+ ///
+ /// Splits the file name into the directory, name and extension parts.
+ /// The name is assumed to have had back slashes replaced by forward
+ /// slashes (if appropriate).
+ ///
+ /// \param full_name Name to split
+ /// \param directory Returned directory part
+ /// \param name Returned name part
+ /// \param extension Returned extension part
+ void split(const std::string& full_name, std::string& directory,
+ std::string& name, std::string& extension) const;
+
+ // Members
+
+ std::string full_name_; ///< Given name
+ std::string directory_; ///< Directory part
+ std::string name_; ///< Name part
+ std::string extension_; ///< Extension part
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __FILENAME_H
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
new file mode 100644
index 0000000..494e7bc
--- /dev/null
+++ b/src/lib/log/logger.cc
@@ -0,0 +1,307 @@
+// Copyright (C) 2010 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
+
+// $Id$
+
+#include <iostream>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <log4cxx/appender.h>
+#include <log4cxx/basicconfigurator.h>
+#include <log4cxx/patternlayout.h>
+#include <log4cxx/consoleappender.h>
+
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+#include <log/strutil.h>
+#include <log/xdebuglevel.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Static initializations
+
+bool Logger::init_ = false;
+
+// Destructor. Delete log4cxx stuff if "don't delete" is clear.
+
+Logger::~Logger() {
+ if (exit_delete_) {
+ delete loggerptr_;
+ }
+}
+
+// Initialize logger - create a logger as a child of the root logger. With
+// log4cxx this is assured by naming the logger <parent>.<child>.
+
+void
+Logger::initLogger() {
+
+ // Initialize basic logging if not already done. This is a one-off for
+ // all loggers.
+ if (!init_) {
+
+ // TEMPORARY
+ // Add a suitable console logger to the log4cxx root logger. (This
+ // is the logger at the root of the log4cxx tree, not the BIND-10 root
+ // logger, which is one level down.) The chosen format is:
+ //
+ // YYYY-MM-DD hh:mm:ss.sss [logger] SEVERITY: text
+ //
+ // As noted, this is a temporary hack: it is done here to ensure that
+ // a suitable output and output pattern is set. Future versions of the
+ // software will set this based on configuration data.
+
+ log4cxx::LayoutPtr layout(
+ new log4cxx::PatternLayout(
+ "%d{yyyy-MM-DD HH:mm:ss.SSS} %-5p [%c] %m\n"));
+ log4cxx::AppenderPtr console(
+ new log4cxx::ConsoleAppender(layout));
+ log4cxx::LoggerPtr sys_root_logger = log4cxx::Logger::getRootLogger();
+ sys_root_logger->addAppender(console);
+
+ // Set the default logging to INFO
+ sys_root_logger->setLevel(log4cxx::Level::getInfo());
+
+ // All static stuff initialized
+ init_ = true;
+ }
+
+ // Initialize this logger. Name this as to whether the BIND-10 root logger
+ // name has been set. (If not, this mucks up the hierarchy :-( ).
+ string root_name = RootLoggerName::getName();
+ if (root_name.empty() || (name_ == root_name)) {
+ loggerptr_ = new log4cxx::LoggerPtr(log4cxx::Logger::getLogger(name_));
+ }
+ else {
+ loggerptr_ = new log4cxx::LoggerPtr(
+ log4cxx::Logger::getLogger(root_name + "." + name_)
+ );
+ }
+}
+
+
+// Set the severity for logging. There is a 1:1 mapping between the logging
+// severity and the log4cxx logging levels, apart from DEBUG.
+//
+// In log4cxx, each of the logging levels (DEBUG, INFO, WARN etc.) has a numeric
+// value. The level is set to one of these and any numeric level equal to or
+// above it that is reported. For example INFO has a value of 20000 and ERROR
+// a value of 40000. So if a message of WARN severity (= 30000) is logged, it is
+// not logged when the logger's severity level is ERROR (as 30000 !>= 40000).
+// It is reported if the logger's severity level is set to WARN (as 30000 >=
+/// 30000) or INFO (30000 >= 20000).
+//
+// This gives a simple system for handling different debug levels. The debug
+// level is a number between 0 and 99, with 0 being least verbose and 99 the
+// most. To implement this seamlessly, when DEBUG is set, the numeric value
+// of the logging level is actually set to (DEBUG - debug-level). Similarly
+// messages of level "n" are logged at a logging level of (DEBUG - n). Thus if
+// the logging level is set to DEBUG and the debug level set to 25, the actual
+// level set is 10000 - 25 = 99975.
+//
+// Attempting to log a debug message of level 26 is an attempt to log a message
+// of level 10000 - 26 = 9974. As 9974 !>= 9975, it is not logged. A
+// message of level 25 is, because 9975 >= 9975.
+//
+// The extended set of logging levels is implemented by the XDebugLevel class.
+
+void
+Logger::setSeverity(Severity severity, int dbglevel) {
+ switch (severity) {
+ case NONE:
+ getLogger()->setLevel(log4cxx::Level::getOff());
+ break;
+
+ case FATAL:
+ getLogger()->setLevel(log4cxx::Level::getFatal());
+ break;
+
+ case ERROR:
+ getLogger()->setLevel(log4cxx::Level::getError());
+ break;
+
+ case WARN:
+ getLogger()->setLevel(log4cxx::Level::getWarn());
+ break;
+
+ case INFO:
+ getLogger()->setLevel(log4cxx::Level::getInfo());
+ break;
+
+ case DEBUG:
+ getLogger()->setLevel(
+ log4cxx::XDebugLevel::getExtendedDebug(dbglevel));
+ break;
+
+ // Will get here for DEFAULT or any other value. This disables the
+ // logger's own severity and it defaults to the severity of the parent
+ // logger.
+ default:
+ getLogger()->setLevel(0);
+ }
+}
+
+// Convert between numeric log4cxx logging level and BIND-10 logging severity.
+
+Logger::Severity
+Logger::convertLevel(int value) const {
+
+ // The order is optimised. This is only likely to be called when testing
+ // for writing debug messages, so the check for DEBUG_INT is first.
+ if (value <= log4cxx::Level::DEBUG_INT) {
+ return (DEBUG);
+ } else if (value <= log4cxx::Level::INFO_INT) {
+ return (INFO);
+ } else if (value <= log4cxx::Level::WARN_INT) {
+ return (WARN);
+ } else if (value <= log4cxx::Level::ERROR_INT) {
+ return (ERROR);
+ } else if (value <= log4cxx::Level::FATAL_INT) {
+ return (FATAL);
+ } else {
+ return (NONE);
+ }
+}
+
+
+// Return the logging severity associated with this logger.
+
+Logger::Severity
+Logger::getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
+ bool check_parent) const {
+
+ log4cxx::LevelPtr level = ptrlogger->getLevel();
+ if (level == log4cxx::LevelPtr()) {
+
+ // Null level returned, logging should be that of the parent.
+
+ if (check_parent) {
+ log4cxx::LoggerPtr parent = ptrlogger->getParent();
+ if (parent == log4cxx::LoggerPtr()) {
+
+ // No parent, so reached the end of the chain. Return INFO
+ // severity.
+ return (INFO);
+ }
+ else {
+ return getSeverityCommon(parent, check_parent);
+ }
+ }
+ else {
+ return (DEFAULT);
+ }
+ } else {
+ return convertLevel(level->toInt());
+ }
+}
+
+
+// Get the debug level. This returns 0 unless the severity is DEBUG.
+
+int
+Logger::getDebugLevel() {
+
+ log4cxx::LevelPtr level = getLogger()->getLevel();
+ if (level == log4cxx::LevelPtr()) {
+
+ // Null pointer returned, logging should be that of the parent.
+ return (0);
+
+ } else {
+ int severity = level->toInt();
+ if (severity <= log4cxx::Level::DEBUG_INT) {
+ return (log4cxx::Level::DEBUG_INT - severity);
+ }
+ else {
+ return (0);
+ }
+ }
+}
+
+// Log an error message:
+// Common code. Owing to the use of variable arguments, this must be inline
+// (hence the definition of the macro). Also note that it expects that the
+// message buffer "message" is declared in the compilation unit.
+
+#define MESSAGE_SIZE (256)
+
+#define FORMAT_MESSAGE(message) \
+ { \
+ MessageDictionary* global = MessageDictionary::globalDictionary(); \
+ string format = global->getText(ident); \
+ va_list ap; \
+ va_start(ap, ident); \
+ vsnprintf(message, sizeof(message), format.c_str(), ap); \
+ message[sizeof(message) - 1] = '\0'; \
+ va_end(ap); \
+ }
+
+
+// Output methods
+
+void
+Logger::debug(int dbglevel, isc::log::MessageID ident, ...) {
+ if (isDebugEnabled(dbglevel)) {
+ char message[MESSAGE_SIZE];
+ FORMAT_MESSAGE(message);
+ LOG4CXX_DEBUG(getLogger(), ident << ", " << message);
+ }
+}
+
+void
+Logger::info(isc::log::MessageID ident, ...) {
+ if (isInfoEnabled()) {
+ char message[MESSAGE_SIZE];
+ FORMAT_MESSAGE(message);
+ LOG4CXX_INFO(getLogger(), ident << ", " << message);
+ }
+}
+
+void
+Logger::warn(isc::log::MessageID ident, ...) {
+ if (isWarnEnabled()) {
+ char message[MESSAGE_SIZE];
+ FORMAT_MESSAGE(message);
+ LOG4CXX_WARN(getLogger(), ident << ", " << message);
+ }
+}
+
+void
+Logger::error(isc::log::MessageID ident, ...) {
+ if (isErrorEnabled()) {
+ char message[MESSAGE_SIZE];
+ FORMAT_MESSAGE(message);
+ LOG4CXX_ERROR(getLogger(), ident << ", " << message);
+ }
+}
+
+void
+Logger::fatal(isc::log::MessageID ident, ...) {
+ if (isFatalEnabled()) {
+ char message[MESSAGE_SIZE];
+ FORMAT_MESSAGE(message);
+ LOG4CXX_FATAL(getLogger(), ident << ", " << message);
+ }
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
new file mode 100644
index 0000000..f26f6d1
--- /dev/null
+++ b/src/lib/log/logger.h
@@ -0,0 +1,327 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __LOGGER_H
+#define __LOGGER_H
+
+#include <cstdlib>
+#include <string>
+#include <boost/lexical_cast.hpp>
+#include <log4cxx/logger.h>
+
+#include <log/dbglevels.h>
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+class Logger {
+public:
+
+ /// \brief Severity Levels
+ typedef enum {
+ DEFAULT, // Default to logging level of parent
+ DEBUG,
+ INFO,
+ WARN,
+ ERROR,
+ FATAL,
+ NONE // Disable logging
+ } Severity;
+
+ /// \brief Constructor
+ ///
+ /// Creates/attaches to a logger of a specific name.
+ ///
+ /// \param name Name of the logger. If the name is that of the root name,
+ /// this creates an instance of the root logger; otherwise it creates a
+ /// child of the root logger.
+ ///
+ /// \param exit_delete This argument is present to get round a bug in
+ /// log4cxx. If a log4cxx logger is declared outside an execution unit, it
+ /// is not deleted until the program runs down. At that point all such
+ /// objects - including internal log4cxx objects - are deleted. However,
+ /// there seems to be a bug in log4cxx where the way that such objects are
+ /// destroyed causes a MutexException to be thrown (this is described in
+ /// https://issues.apache.org/jira/browse/LOGCXX-322). As this only occurs
+ /// during program rundown, the issue is not serious - it just looks bad to
+ /// have the program crash instead of shut down cleanly.\n
+ /// \n
+ /// The original implementation of the isc::log::Logger had as a member a
+ /// log4cxx logger (actually a LoggerPtr). If the isc:: Logger was declared
+ /// statically, when it was destroyed at the end of the program the internal
+ /// LoggerPtr was destroyed, which triggered the problem. The problem did
+ /// not occur if the isc::log::Logger was created on the stack. To get
+ /// round this, the internal LoggerPtr is now created dynamically. The
+ /// exit_delete argument controls its destruction: if true, it is destroyed
+ /// in the ISC Logger destructor. If false, it is not.\n
+ /// \n
+ /// When creating an isc::log::Logger on the stack, the argument should be
+ /// false (the default); when the Logger is destroyed, all the internal
+ /// log4cxx objects are destroyed. As only the logger (and not the internal
+ /// log4cxx data structures are being destroyed), all is well. However,
+ /// when creating the logger statically, the argument should be false. This
+ /// means that the log4cxx objects are not destroyed at program rundown;
+ /// instead memory is reclaimed and files are closed when the process is
+ /// destroyed, something that does not trigger the bug.
+ Logger(const std::string& name, bool exit_delete = false) :
+ loggerptr_(), name_(name), exit_delete_(exit_delete)
+ {}
+
+
+ /// \brief Destructor
+ virtual ~Logger();
+
+
+ /// \brief Configure Options
+ ///
+ /// TEMPORARY: Pass in the command-line options to set the logging severity
+ /// for the root logger. Future versions of the logger will get this
+ /// information from the configuration database.
+ ///
+ /// \param severity Severity level to log
+ /// \param dbglevel If the severity is DEBUG, this is the debug level.
+ /// This can be in the range 1 to 100 and controls the verbosity. A value
+ /// outside these limits is silently coerced to the nearest boundary.
+ /// \param local_file If provided, the name of a message file to read in and
+ /// supersede one or more of the current messages.
+ static void runTimeInit(Severity severity = INFO, int dbglevel = 1,
+ const char* local_file = NULL);
+
+
+ /// \brief Get Name of Logger
+ ///
+ /// \return The full name of the logger (including the root name)
+ virtual std::string getName() {
+ return getLogger()->getName();
+ }
+
+
+ /// \brief Set Severity Level for Logger
+ ///
+ /// Sets the level at which this logger will log messages. If none is set,
+ /// the level is inherited from the parent.
+ ///
+ /// \param severity Severity level to log
+ /// \param dbglevel If the severity is DEBUG, this is the debug level.
+ /// This can be in the range 1 to 100 and controls the verbosity. A value
+ /// outside these limits is silently coerced to the nearest boundary.
+ virtual void setSeverity(Severity severity, int dbglevel = 1);
+
+
+ /// \brief Get Severity Level for Logger
+ ///
+ /// \return The current logging level of this logger. In most cases though,
+ /// the effective logging level is what is required.
+ virtual Severity getSeverity() {
+ return getSeverityCommon(getLogger(), false);
+ }
+
+ /// \brief Get Effective Severity Level for Logger
+ ///
+ /// \return The effective severity level of the logger. This is the same
+ /// as getSeverity() if the logger has a severity level set, but otherwise
+ /// is the severity of the parent.
+ virtual Severity getEffectiveSeverity() {
+ return getSeverityCommon(getLogger(), true);
+ }
+
+
+ /// \brief Return DEBUG Level
+ ///
+ /// \return Current setting of debug level. This is returned regardless of
+ /// whether the
+ virtual int getDebugLevel();
+
+
+ /// \brief Returns if Debug Message Should Be Output
+ ///
+ /// \param dbglevel Level for which debugging is checked. Debugging is
+ /// enabled only if the logger has DEBUG enabled and if the dbglevel
+ /// checked is less than or equal to the debug level set for the logger.
+ virtual bool
+ isDebugEnabled(int dbglevel = MIN_DEBUG_LEVEL) {
+ return (getLogger()->getEffectiveLevel()->toInt() <=
+ (log4cxx::Level::DEBUG_INT - dbglevel));
+ }
+
+
+ /// \brief Is INFO Enabled?
+ virtual bool isInfoEnabled() {
+ return (getLogger()->isInfoEnabled());
+ }
+
+
+ /// \brief Is WARNING Enabled?
+ virtual bool isWarnEnabled() {
+ return (getLogger()->isWarnEnabled());
+ }
+
+
+ /// \brief Is ERROR Enabled?
+ virtual bool isErrorEnabled() {
+ return (getLogger()->isErrorEnabled());
+ }
+
+
+ /// \brief Is FATAL Enabled?
+ virtual bool isFatalEnabled() {
+ return (getLogger()->isFatalEnabled());
+ }
+
+
+ /// \brief Output Debug Message
+ ///
+ /// \param dbglevel Debug level, ranging between 0 and 99. Higher numbers
+ /// are used for more verbose output.
+ /// \param ident Message identification.
+ /// \param ... Optional arguments for the message.
+ void debug(int dbglevel, MessageID ident, ...);
+
+
+ /// \brief Output Informational Message
+ ///
+ /// \param ident Message identification.
+ /// \param ... Optional arguments for the message.
+ void info(MessageID ident, ...);
+
+
+ /// \brief Output Warning Message
+ ///
+ /// \param ident Message identification.
+ /// \param ... Optional arguments for the message.
+ void warn(MessageID ident, ...);
+
+
+ /// \brief Output Error Message
+ ///
+ /// \param ident Message identification.
+ /// \param ... Optional arguments for the message.
+ void error(MessageID ident, ...);
+
+
+ /// \brief Output Fatal Message
+ ///
+ /// \param ident Message identification.
+ /// \param ... Optional arguments for the message.
+ void fatal(MessageID ident, ...);
+
+
+protected:
+
+ /// \brief Equality
+ ///
+ /// Check if two instances of this logger refer to the same stream.
+ /// (This method is principally for testing.)
+ ///
+ /// \return true if the logger objects are instances of the same logger.
+ bool operator==(const Logger& other) const {
+ return (*loggerptr_ == *other.loggerptr_);
+ }
+
+
+ /// \brief Logger Initialized
+ ///
+ /// Check that the logger has been properly initialized. (This method
+ /// is principally for testing.)
+ ///
+ /// \return true if this logger object has been initialized.
+ bool isInitialized() const {
+ return (loggerptr_ != NULL);
+ }
+
+
+ /// \brief Get Severity Level for Logger
+ ///
+ /// This is common code for getSeverity() and getEffectiveSeverity() -
+ /// it returns the severity of the logger; if not set (and the check_parent)
+ /// flag is set, it searches up the parent-child tree until a severity
+ /// level is found and uses that.
+ ///
+ /// \param ptrlogger Pointer to the log4cxx logger to check.
+ /// \param check_parent true to search up the tree, false to return the
+ /// current level.
+ ///
+ /// \return The effective severity level of the logger. This is the same
+ /// as getSeverity() if the logger has a severity level set, but otherwise
+ /// is the severity of the parent.
+ Logger::Severity getSeverityCommon(const log4cxx::LoggerPtr& ptrlogger,
+ bool check_parent) const;
+
+
+ /// \brief Convert Between BIND-10 and log4cxx Logging Levels
+ ///
+ /// Converts between the numeric value of the log4cxx logging level
+ /// and the BIND-10 severity level.
+ ///
+ /// \param value log4cxx numeric logging level
+ ///
+ /// \return BIND-10 logging severity
+ Severity convertLevel(int value) const;
+
+
+ /// \brief Initialize log4cxx Logger
+ ///
+ /// Creates the log4cxx logger used internally. A function is provided for
+ /// this so that the creation does not take place when this Logger object
+ /// is created but when it is used. As the latter occurs in executable
+ /// code but the former can occur during initialization, this order
+ /// guarantees that anything that is statically initialized has completed
+ /// its initialization by the time the logger is used.
+ void initLogger();
+
+
+ /// \brief Return log4cxx Logger
+ ///
+ /// Returns the log4cxx logger, initializing it if not already initialized.
+ ///
+ /// \return Loggerptr object
+ log4cxx::LoggerPtr& getLogger() {
+ if (loggerptr_ == NULL) {
+ initLogger();
+ }
+ return *loggerptr_;
+ }
+
+
+ /// \brief Read Local Message File
+ ///
+ /// Reads a local message file into the global dictionary, replacing any
+ /// definitions there. Any messages found in the local file that do not
+ /// replace ones in the global dictionary are listed.
+ ///
+ /// \param file Local message file to be read.
+ static void readLocalMessageFile(const char* file);
+
+private:
+ // Note that loggerptr_ is a pointer to a LoggerPtr, which is itself a
+ // pointer to the underlying log4cxx logger. This is due to the problems
+ // with memory deletion on program exit, explained in the comments for
+ // the "exit_delete" parameter in this class's constructor.
+
+ log4cxx::LoggerPtr* loggerptr_; ///< Pointer to the underlying logger
+ std::string name_; ///< Name of this logger]
+ bool exit_delete_; ///< Delete loggerptr_ on exit?
+
+ // NOTE - THIS IS A PLACE HOLDER
+ static bool init_; ///< Set true when initialized
+};
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_H
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
new file mode 100644
index 0000000..c9ba858
--- /dev/null
+++ b/src/lib/log/logger_support.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2010 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
+
+// $Id$
+
+
+
+/// \brief Temporary Logger Support
+///
+/// Performs run-time initialization of the logger system. In particular, it
+/// is passed information from the command line and:
+///
+/// a) Sets the severity of the messages being logged (and debug level if
+/// appropriate).
+/// b) Reads in the local message file is one has been supplied.
+///
+/// These functions will be replaced once the code has bneen written to obtain
+/// the logging parameters from the configuration database.
+
+#include <vector>
+
+#include <log/logger.h>
+#include <log/logger_support.h>
+#include <log/messagedef.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_reader.h>
+#include <log/message_types.h>
+#include <log/root_logger_name.h>
+
+namespace isc {
+namespace log {
+
+using namespace std;
+
+// Declare a logger for the logging subsystem
+Logger logger("log");
+
+
+/// \brief Reads Local Message File
+///
+/// Reads the local message file into the global dictionary, overwriting
+/// existing messages. If the file contained any message IDs not in the
+/// dictionary, they are listed in a warning message.
+///
+/// \param file Name of the local message file
+static void
+readLocalMessageFile(const char* file) {
+
+ MessageDictionary* dictionary = MessageDictionary::globalDictionary();
+ MessageReader reader(dictionary);
+ try {
+ reader.readFile(file, MessageReader::REPLACE);
+
+ // File successfully read, list the duplicates
+ MessageReader::MessageIDCollection unknown = reader.getNotAdded();
+ for (MessageReader::MessageIDCollection::const_iterator
+ i = unknown.begin(); i != unknown.end(); ++i) {
+ logger.warn(MSG_IDNOTFND, (*i).c_str());
+ }
+ }
+ catch (MessageException& e) {
+ MessageID ident = e.id();
+ vector<MessageID> args = e.arguments();
+ switch (args.size()) {
+ case 0:
+ logger.error(ident);
+ break;
+
+ case 1:
+ logger.error(ident, args[0].c_str());
+ break;
+
+ default: // 2 or more (2 should be the maximum)
+ logger.error(ident, args[0].c_str(), args[1].c_str());
+ }
+ }
+}
+
+/// Logger Run-Time Initialization
+
+void
+runTimeInit(Logger::Severity severity, int dbglevel, const char* file) {
+
+ // Create the application root logger. This is the logger that has the
+ // name of the application (and is one level down from the log4cxx root
+ // logger). All other loggers created in this application will be its
+ // child.
+ //
+ // The main purpose of the application root logger is to provide the root
+ // name in output message for all other loggers.
+ Logger logger(RootLoggerName::getName());
+
+ // Set the severity associated with it. If no other logger has a severity,
+ // this will be the default.
+ logger.setSeverity(severity, dbglevel);
+
+ // Replace any messages with local ones (if given)
+ if (file) {
+ readLocalMessageFile(file);
+ }
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
new file mode 100644
index 0000000..85f838f
--- /dev/null
+++ b/src/lib/log/logger_support.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __LOGGER_SUPPORT_H
+#define __LOGGER_SUPPORT_H
+
+#include <log/logger.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Run-Time Initialization
+///
+/// This code will be used until the logger is fully integrated into the BIND-10
+/// configuration database. It performs run-time initialization of th logger,
+/// in particular supplying run-time choices to it:
+///
+/// * The severity (and if applicable, debug level) at which to log
+/// * Name of a local message file, containing localisation of message text.
+///
+/// \param severity Severity at which to log
+/// \param dbglevel Debug severiy (ignored if "severity" is not "DEBUG")
+/// \param file Name of the local message file.
+void runTimeInit(Logger::Severity severity, int dbglevel, const char* file);
+
+} // namespace log
+} // namespace isc
+
+
+#endif // __LOGGER_SUPPORT_H
diff --git a/src/lib/log/message_dictionary.cc b/src/lib/log/message_dictionary.cc
new file mode 100644
index 0000000..05c79f8
--- /dev/null
+++ b/src/lib/log/message_dictionary.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <cstddef>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// (Virtual) Destructor
+
+MessageDictionary::~MessageDictionary() {
+}
+
+// Add message and note if ID already exists
+
+bool
+MessageDictionary::add(const MessageID& ident, const std::string& text) {
+ map<MessageID, string>::iterator i = dictionary_.find(ident);
+ bool not_found = (i == dictionary_.end());
+ if (not_found) {
+
+ // Message not already in the dictionary, so add it.
+ dictionary_[ident] = text;
+ }
+
+ return (not_found);
+}
+
+// Add message and note if ID does not already exist
+
+bool
+MessageDictionary::replace(const MessageID& ident, const std::string& text) {
+ map<MessageID, string>::iterator i = dictionary_.find(ident);
+ bool found = (i != dictionary_.end());
+ if (found) {
+
+ // Exists, so replace it.
+ dictionary_[ident] = text;
+ }
+
+ return (found);
+}
+
+// Load a set of messages
+
+vector<MessageID>
+MessageDictionary::load(const char* messages[]) {
+ vector<MessageID> duplicates;
+ int i = 0;
+ while (messages[i]) {
+
+ // ID present, so note it and point to text.
+ MessageID ident(messages[i++]);
+ if (messages[i]) {
+
+ // Text not null, note it and point to next ident.
+ string text(messages[i++]);
+
+ // Add ID and text to message dictionary, noting if the ID was
+ // already present.
+ bool added = add(ident, text);
+ if (!added) {
+ duplicates.push_back(ident);
+ }
+ }
+ }
+ return duplicates;
+}
+
+// Return message text or blank string
+
+string
+MessageDictionary::getText(const MessageID& ident) const {
+ map<MessageID, string>::const_iterator i = dictionary_.find(ident);
+ if (i == dictionary_.end()) {
+ return string("");
+ }
+ else {
+ return i->second;
+ }
+}
+
+// Return global dictionary
+
+MessageDictionary*
+MessageDictionary::globalDictionary() {
+ static MessageDictionary* global = NULL;
+
+ if (global == NULL) {
+ global = new MessageDictionary();
+ }
+ return global;
+}
+
+
+
+
+} // namspace log
+} // namespace isc
diff --git a/src/lib/log/message_dictionary.h b/src/lib/log/message_dictionary.h
new file mode 100644
index 0000000..0b2e704
--- /dev/null
+++ b/src/lib/log/message_dictionary.h
@@ -0,0 +1,150 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_DICTIONARY_H
+#define __MESSAGE_DICTIONARY_H
+
+#include <cstddef>
+#include <string>
+#include <map>
+#include <vector>
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Message Dictionary
+///
+/// The message dictionary is a wrapper around a std::map object, and allows
+/// message text to be retrieved given the string identification.
+///
+/// Adding text occurs in two modes:
+///
+/// Through the "Add" method, ID/text mappings are added to the dictionary
+/// unless the ID already exists. This is designed for use during program
+/// initialization, where a local message may supplant a compiled-in message.
+///
+/// Through the "Replace" method, ID/text mappings are added to the dictionary
+/// only if the ID already exists. This is for use when a message file is
+/// supplied to replace messages provided with the program.
+///
+/// Although the class can be used stand-alone, it does supply a static method
+/// to return a particular instance - the "global" dictionary.
+
+class MessageDictionary {
+public:
+
+ // Default constructor and assignment operator are OK for this class
+
+ /// \brief Virtual Destructor
+ virtual ~MessageDictionary();
+
+ /// \brief Add Message
+ ///
+ /// Adds a message to the dictionary. If the ID already exists, the ID is
+ /// added to the overflow vector.
+ ///
+ /// \param ident Identification of the message to add
+ /// \param text Message text
+ ///
+ /// \return true if the message was added to the dictionary, false if the
+ /// message existed and it was not added.
+ virtual bool add(const MessageID& ident, const std::string& text);
+
+
+ /// \brief Replace Message
+ ///
+ /// Replaces a message in the dictionary. If the ID does not exist, it is
+ /// added to the overflow vector.
+ ///
+ /// \param ident Identification of the message to replace
+ /// \param text Message text
+ ///
+ /// \return true if the message was added to the dictionary, false if the
+ /// message did not exist and it was not added.
+ virtual bool replace(const MessageID& ident, const std::string& text);
+
+
+ /// \brief Load Dictionary
+ ///
+ /// Designed to be used during the initialization of programs, this
+ /// accepts a set of (ID, text) pairs as a one-dimensional array of
+ /// const char* and adds them to the dictionary. The messages are added
+ /// using "Add".
+ ///
+ /// \param data null-terminated array of const char* alternating ID and
+ /// message text. This should be an odd number of elements long, the last
+ /// elemnent being NULL. If it is an even number of elements long, the
+ /// last ID is ignored.
+ ///
+ /// \return Vector of message IDs that were not loaded because an ID of the
+ /// same name already existing in the dictionary. This vector may be
+ /// empty.
+ virtual std::vector<MessageID> load(const char* elements[]);
+
+
+ /// \brief Get Message Text
+ ///
+ /// Given an ID, retrieve associated message text.
+ ///
+ /// \param ident Message identification
+ ///
+ /// \return Text associated with message or empty string if the ID is not
+ /// recognised. (Note: this precludes an ID being associated with an empty
+ /// string.)
+ virtual std::string getText(const MessageID& ident) const;
+
+
+ /// \brief Number of Items in Dictionary
+ ///
+ /// \return Number of items in the dictionary
+ virtual size_t size() const {
+ return dictionary_.size();
+ }
+
+
+ // Allow access to the internal map structure, but don't allow alteration.
+ typedef std::map<MessageID, std::string>::const_iterator const_iterator;
+
+
+ /// \brief Return begin() iterator of internal map
+ const_iterator begin() const {
+ return dictionary_.begin();
+ }
+
+
+ /// \brief Return end() iterator of internal map
+ const_iterator end() const {
+ return dictionary_.end();
+ }
+
+
+ /// \brief Return Global Dictionary
+ ///
+ /// Returns a pointer to the singleton global dictionary.
+ ///
+ /// \return Pointer to global dictionary.
+ static MessageDictionary* globalDictionary();
+
+private:
+ std::map<MessageID, std::string> dictionary_;
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGE_DICTIONARY_H
diff --git a/src/lib/log/message_exception.cc b/src/lib/log/message_exception.cc
new file mode 100644
index 0000000..562c381
--- /dev/null
+++ b/src/lib/log/message_exception.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+/// \brief Body of Virtual Destructor
+
+#include <log/message_exception.h>
+
+namespace isc {
+namespace log {
+
+MessageException::~MessageException() throw() {
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
new file mode 100644
index 0000000..537392d
--- /dev/null
+++ b/src/lib/log/message_exception.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_EXCEPTION_H
+#define __MESSAGE_EXCEPTION_H
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Message Exception
+///
+/// Used in the message reader, this simple exception class allows a message
+/// code and its arguments to be encapsulated in an exception and thrown
+/// up the stack.
+
+class MessageException : public std::exception {
+public:
+
+ /// \brief Constructor
+ ///
+ /// \param id Message identification
+ MessageException(MessageID id) : id_(id)
+ {}
+
+ /// \brief Constructor
+ ///
+ /// \param id Message identification
+ /// \param arg1 First message argument
+ MessageException(MessageID id, const std::string& arg1) : id_(id)
+ {
+ args_.push_back(arg1);
+ }
+
+ /// \brief Constructor
+ ///
+ /// \param id Message identification
+ /// \param arg1 First message argument
+ /// \param arg2 Second message argument
+ MessageException(MessageID id, const std::string& arg1,
+ const std::string& arg2) : id_(id)
+ {
+ args_.push_back(arg1);
+ args_.push_back(arg2);
+ }
+
+ /// \brief Destructor
+ virtual ~MessageException() throw();
+
+ /// \brief Return Message ID
+ ///
+ /// \return Message identification
+ MessageID id() const {
+ return id_;
+ }
+
+ /// \brief Return Arguments
+ ///
+ /// \return Exception Arguments
+ std::vector<std::string> arguments() const {
+ return args_;
+ }
+
+private:
+ MessageID id_; // Exception ID
+ std::vector<std::string> args_; // Exception arguments
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGE_EXCEPTION_H
diff --git a/src/lib/log/message_initializer.cc b/src/lib/log/message_initializer.cc
new file mode 100644
index 0000000..914ed17
--- /dev/null
+++ b/src/lib/log/message_initializer.cc
@@ -0,0 +1,32 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <log/message_dictionary.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace log {
+
+// Constructor. Just retrieve the global dictionary and load the IDs and
+// associated text into it.
+
+MessageInitializer::MessageInitializer(const char* values[]) {
+ MessageDictionary* global = MessageDictionary::globalDictionary();
+ global->load(values);
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/message_initializer.h b/src/lib/log/message_initializer.h
new file mode 100644
index 0000000..a776a02
--- /dev/null
+++ b/src/lib/log/message_initializer.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGEINITIALIZER_H
+#define __MESSAGEINITIALIZER_H
+
+#include <log/message_dictionary.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Initialize Message Dictionary
+///
+/// This is a helper class to add a set of message IDs and associated text to
+/// the global dictionary.
+///
+/// It should be declared outside an execution unit and initialized with a
+/// an array of values, alternating identifier, associated text and ending with
+/// a NULL, e.g.
+///
+/// static const char* values[] = {
+/// "IDENT1", "message for ident 1",
+/// "IDENT2", "message for ident 2",
+/// :
+/// NULL
+/// };
+/// MessageDictionaryHelper xyz(values);
+///
+/// This will automatically add the message ID/text pairs to the global
+/// dictionary during initialization - all that is required is that the module
+/// containing the definition is included into the final executable.
+///
+/// Messages are added via the MessageDictionary::add() method, so any
+/// duplicates are stored in the the global dictionary's overflow vector whence
+/// they can be retrieved at run-time.
+
+class MessageInitializer {
+public:
+
+ /// \brief Constructor
+ ///
+ /// The only method in the class, this adds the array of values to the
+ /// global dictionary.
+ MessageInitializer(const char* values[]);
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGEINITIALIZER_H
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
new file mode 100644
index 0000000..203b836
--- /dev/null
+++ b/src/lib/log/message_reader.cc
@@ -0,0 +1,184 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <errno.h>
+#include <string.h>
+
+#include <iostream>
+#include <fstream>
+
+#include <log/message_exception.h>
+#include <log/messagedef.h>
+#include <log/message_reader.h>
+#include <log/strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Virtual destructor.
+MessageReader::~MessageReader() {
+}
+
+
+// Read the file.
+
+void
+MessageReader::readFile(const string& file, MessageReader::Mode mode) {
+
+ // Ensure the non-added collection is empty: this object might be
+ // being reused.
+ not_added_.clear();
+
+ // Open the file
+ ifstream infile(file.c_str());
+ if (infile.fail()) {
+ throw MessageException(MSG_OPENIN, file, strerror(errno));
+ }
+
+ // Loop round reading it.
+ string line;
+ getline(infile, line);
+ while (infile.good()) {
+ processLine(line, mode);
+ getline(infile, line);
+ }
+
+ // Why did the loop terminate?
+ if (!infile.eof()) {
+ throw MessageException(MSG_READERR, file, strerror(errno));
+ }
+ infile.close();
+}
+
+// Parse a line of the file
+
+void
+MessageReader::processLine(const string& line, MessageReader::Mode mode) {
+
+ // Get rid of leading and trailing spaces
+ string text = isc::strutil::trim(line);
+
+ if (text.empty()) {
+ ; // Ignore blank lines
+
+ } else if ((text[0] == '#') || (text[0] == '+')) {
+ ; // Ignore comments or descriptions
+
+ } else if (text[0] == '$') {
+ parseDirective(text); // Process directives
+
+ } else {
+ parseMessage(text, mode); // Process other lines
+
+ }
+}
+
+// Process directive
+
+void
+MessageReader::parseDirective(const std::string& text) {
+
+ static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+
+ // Regardless of what happens, all prefixes will be uppercase (as will
+ // all symbols).
+ string line = text;
+ isc::strutil::uppercase(line);
+ vector<string> tokens = isc::strutil::tokens(line);
+
+ // Only $PREFIX is recognised so far, so we'll handle it here.
+ if (tokens[0] != string("$PREFIX")) {
+ throw MessageException(MSG_UNRECDIR, tokens[0]);
+
+ } else if (tokens.size() < 2) {
+ throw MessageException(MSG_PRFNOARG);
+
+ } else if (tokens.size() > 2) {
+ throw MessageException(MSG_PRFEXTRARG);
+
+ }
+
+ // Token is potentially valid providing it only contains alphabetic
+ // and numeric characters (and underscores) and does not start with a
+ // digit.
+
+ if ((tokens[1].find_first_not_of(valid) != string::npos) ||
+ (std::isdigit(tokens[1][0]))) {
+
+ // Invalid character in string or it starts with a digit.
+ throw MessageException(MSG_PRFINVARG, tokens[1]);
+ }
+
+ // All OK - unless the prefix has already been set.
+
+ if (prefix_.size() != 0) {
+ throw MessageException(MSG_DUPLPRFX);
+ }
+
+ // Prefix has not been set, so set it and return success.
+
+ prefix_ = tokens[1];
+}
+
+// Process message. By the time this method is called, the line has been
+// stripped of leading and trailing spaces, and we believe that it is a line
+// defining a message. The first token on the line is convered to uppercase
+// and becomes the message ID; the rest of the line is the message text.
+
+void
+MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
+
+ static string delimiters("\t\n "); // Delimiters
+
+ // Look for the first delimiter.
+ size_t first_delim = text.find_first_of(delimiters);
+ if (first_delim == string::npos) {
+
+ // Just a single token in the line - this is not valid
+ throw MessageException(MSG_ONETOKEN, text);
+ }
+
+ // Extract the first token into the message ID
+ MessageID ident = text.substr(0, first_delim);
+
+ // Locate the start of the message text
+ size_t first_text = text.find_first_not_of(delimiters, first_delim);
+ if (first_text == string::npos) {
+
+ // ?? This happens if there are trailing delimiters, which should not
+ // occur as we have stripped trailing spaces off the line. Just treat
+ // this as a single-token error for simplicity's sake.
+ throw MessageException(MSG_ONETOKEN, text);
+ }
+
+ // Add the result to the dictionary and to the non-added list if the add to
+ // the dictionary fails.
+ bool added;
+ if (mode == ADD) {
+ added = dictionary_->add(ident, text.substr(first_text));
+ }
+ else {
+ added = dictionary_->replace(ident, text.substr(first_text));
+ }
+ if (!added) {
+ not_added_.push_back(ident);
+ }
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
new file mode 100644
index 0000000..84ffce9
--- /dev/null
+++ b/src/lib/log/message_reader.h
@@ -0,0 +1,175 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_READER_H
+#define __MESSAGE_READER_H
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+/// \brief Read Message File
+///
+/// Reads a message file and creates a map of identifier against the text of the
+/// message. This map can be retrieved for subsequent processing.
+
+class MessageReader {
+public:
+
+ /// \brief Read Mode
+ ///
+ /// If ADD, messages are added to the dictionary if the ID does not exist
+ /// there. If it does, the ID is added to the dictionary's overflow
+ /// vector.
+ ///
+ /// If REPLACE, the dictionary is only modified if the message ID already
+ /// exists in it. New message IDs are added to the overflow vector.
+ typedef enum {
+ ADD,
+ REPLACE
+ } Mode;
+
+ /// \brief Visible collection types
+ typedef std::vector<MessageID> MessageIDCollection;
+
+ /// \brief Constructor
+ ///
+ /// Default constructor. All work is done in the main readFile code (so
+ /// that a status return can be returned instead of needing to throw an
+ /// exception).
+ ///
+ /// \param dictionary Dictionary to which messages read read from the file
+ /// are added. (This should be a local dictionary when the class is used in
+ /// the message compiler, and the global dictionary when used in a server.
+ /// The ownership of the dictionary object is not transferred - the caller
+ /// is responsible for managing the lifetime of the dictionary.
+ MessageReader(MessageDictionary* dictionary = NULL) :
+ dictionary_(dictionary)
+ {}
+
+
+ /// \brief Virtual Destructor
+ virtual ~MessageReader();
+
+
+ /// \brief Get Dictionary
+ ///
+ /// Returns the pointer to the dictionary object. Note that ownership is
+ /// not transferred - the caller should not delete it.
+ ///
+ /// \return Pointer to current dictionary object
+ MessageDictionary* getDictionary() const {
+ return dictionary_;
+ }
+
+
+ /// \brief Set Dictionary
+ ///
+ /// Sets the current dictionary object.
+ ///
+ /// \param dictionary New dictionary object. The ownership of the dictionary
+ /// object is not transferred - the caller is responsible for managing the
+ /// lifetime of the dictionary.
+ void setDictionary(MessageDictionary* dictionary) {
+ dictionary_ = dictionary;
+ }
+
+
+ /// \brief Read File
+ ///
+ /// This is the main method of the class and reads in the file, parses it,
+ /// and stores the result in the message dictionary.
+ ///
+ /// \param file Name of the message file.
+ /// \param mode Addition mode. See the description of the "Mode" enum.
+ virtual void readFile(const std::string& file, Mode mode = ADD);
+
+
+ /// \brief Process Line
+ ///
+ /// Parses a text line and adds it to the message map. Although this is
+ /// for use in readFile, it can also be used to add individual messages
+ /// to the message map.
+ ///
+ /// \param line Line of text to process
+ /// \param mode If a message line, how to add the message to the dictionary.
+ virtual void processLine(const std::string& line, Mode mode = ADD);
+
+
+ /// \brief Get Prefix
+ ///
+ /// \return Argument to the $PREFIX directive (if present)
+ virtual std::string getPrefix() const {
+ return prefix_;
+ }
+
+
+ /// \brief Clear Prefix
+ ///
+ /// Clears the current prefix.
+ virtual void clearPrefix() {
+ prefix_ = "";
+ }
+
+
+ /// \brief Get Not-Added List
+ ///
+ /// Returns the list of IDs that were not added during the last
+ /// read of the file.
+ ///
+ /// \return Collection of messages not added
+ MessageIDCollection getNotAdded() const {
+ return not_added_;
+ }
+
+private:
+
+ /// \brief Handle a Message Definition
+ ///
+ /// Passed a line that should contain a message, this processes that line
+ /// and adds it to the dictionary according to the mode setting.
+ ///
+ /// \param line Line of text
+ /// \param ADD or REPLACE depending on how the reader is operating. (See
+ /// the description of the Mode typedef for details.)
+ void parseMessage(const std::string& line, Mode mode);
+
+
+ /// \brief Handle Directive
+ ///
+ /// Passed a line starting with a "$", this handles the processing of
+ /// directives.
+ ///
+ /// \param line Line of text that starts with "$",
+ void parseDirective(const std::string& line);
+
+ /// Attributes
+ MessageDictionary* dictionary_; ///< Dictionary to add messages to
+ MessageIDCollection not_added_; ///< List of IDs not added
+ std::string prefix_; ///< Input of $PREFIX statement
+};
+
+} // namespace log
+} // namespace isc
+
+#endif // __MESSAGE_READER_H
diff --git a/src/lib/log/message_types.h b/src/lib/log/message_types.h
new file mode 100644
index 0000000..b101401
--- /dev/null
+++ b/src/lib/log/message_types.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_TYPES_H
+#define __MESSAGE_TYPES_H
+
+#include <string>
+
+namespace isc {
+namespace log {
+
+typedef std::string MessageID;
+
+} // namespace log
+} // namespace isc
+
+
+
+#endif // __MESSAGE_TYPES_H
diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc
new file mode 100644
index 0000000..7dfa4f6
--- /dev/null
+++ b/src/lib/log/messagedef.cc
@@ -0,0 +1,27 @@
+// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+
+#include <cstddef>
+#include <log/message_initializer.h>
+
+using namespace isc::log;
+
+namespace {
+
+const char* values[] = {
+ "DUPLPRFX", "duplicate $PREFIX directive found",
+ "IDNOTFND", "could not replace message for '%s': no such message identification",
+ "ONETOKEN", "a line containing a message ID ('%s') and nothing else was found",
+ "OPENIN", "unable to open message file %s for input: %s",
+ "OPENOUT", "unable to open %s for output: %s",
+ "PRFEXTRARG", "$PREFIX directive has too many arguments",
+ "PRFINVARG", "$PREFIX directive has an invalid argument ('%s')",
+ "PRFNOARG", "no arguments were given to the $PREFIX directive",
+ "READERR", "error reading from %s: %s",
+ "UNRECDIR", "unrecognised directive '%s'",
+ "WRITERR", "error writing to %s: %s",
+ NULL
+};
+
+} // Anonymous namespace
+
+MessageInitializer messagedef_cc_Mon_Jan_17_15_25_32_2011(values);
diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h
new file mode 100644
index 0000000..ae0a99d
--- /dev/null
+++ b/src/lib/log/messagedef.h
@@ -0,0 +1,24 @@
+// File created from messagedef.mes on Mon Jan 17 15:25:32 2011
+
+#ifndef __MESSAGEDEF_H
+#define __MESSAGEDEF_H
+
+#include <log/message_types.h>
+
+namespace {
+
+isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
+isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
+isc::log::MessageID MSG_ONETOKEN = "ONETOKEN";
+isc::log::MessageID MSG_OPENIN = "OPENIN";
+isc::log::MessageID MSG_OPENOUT = "OPENOUT";
+isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
+isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
+isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
+isc::log::MessageID MSG_READERR = "READERR";
+isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
+isc::log::MessageID MSG_WRITERR = "WRITERR";
+
+} // Anonymous namespace
+
+#endif // __MESSAGEDEF_H
diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes
new file mode 100644
index 0000000..1535fc6
--- /dev/null
+++ b/src/lib/log/messagedef.mes
@@ -0,0 +1,82 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id$
+
+$PREFIX MSG_
+
+# \brief Message Utility Message File
+#
+# This is the source of the set of messages generated by the message and logging
+# components. The associated .h and .cc files are created by hand from this
+# file though and are not built during the build process; this is to avoid the
+# chicken-and-egg situation where we need the files to build the message
+# compiler, yet we need the compiler to build the files.
+
+DUPLPRFX duplicate $PREFIX directive found
++ When reading a message file, more than one $PREFIX directive was found. In
++ this version of the code, such a condition is regarded as an error and the
++ read will be abandonded.
+
+IDNOTFND could not replace message for '%s': no such message identification
++ During start-up a local message file was read. A line with the listed
++ message identification was found in the file, but the identification is not
++ one contained in the compiled-in message dictionary. Either the message
++ identification has been mis-spelled in the file, or the local file was used
++ for an earlier version of the software and the message with that
++ identification has been removed.
++
++ This message may appear a number of times in the file, once for every such
++ unknown mnessage identification.
+
+ONETOKEN a line containing a message ID ('%s') and nothing else was found
++ Message definitions comprise lines starting with a message identification (a
++ symbolic name for the message) and followed by the text of the message. This
++ error is generated when a line is found in the message file that contains just
++ the message identification.
+
+OPENIN unable to open message file %s for input: %s
++ The program was not able to open the specified input message file for the
++ reason given.
+
+OPENOUT unable to open %s for output: %s
++ The program was not able to open the specified output file for the reason
++ given.
+
+PRFEXTRARG $PREFIX directive has too many arguments
++ The $PREFIX directive takes a single argument, a prefix to be added to the
++ symbol names when a C++ .h file is created. This error is generated when the
++ compiler finds a $PREFIX directive with more than one argument.
+
+PRFINVARG $PREFIX directive has an invalid argument ('%s')
++ The $PREFIX argument is used in a symbol name in a C++ header file. As such,
++ it must adhere to restrictions on C++ symbol names (e.g. may only contain
++ alphanumeric characters or underscores, and may nor start with a digit). A
++ $PREFIX directive was found with an argument (given in the message) that
++ violates those restictions.
+
+PRFNOARG no arguments were given to the $PREFIX directive
++ The $PREFIX directive takes a single argument, a prefix to be added to the
++ symbol names when a C++ .h file is created. This error is generated when the
++ compiler finds a $PREFIX directive with noa rguments.
+
+READERR error reading from %s: %s
++ The specified error was encountered reading from the named input file.
+
+UNRECDIR unrecognised directive '%s'
++ A line starting with a dollar symbol was found, but the first word on the line
++ (shown in the message) was not a recognised message compiler directive.
+
+WRITERR error writing to %s: %s
++ The specified error was encountered writing to the named output file.
diff --git a/src/lib/log/root_logger_name.cc b/src/lib/log/root_logger_name.cc
new file mode 100644
index 0000000..9378857
--- /dev/null
+++ b/src/lib/log/root_logger_name.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#include <string>
+#include <root_logger_name.h>
+
+namespace isc {
+namespace log {
+
+std::string RootLoggerName::name_("");
+
+}
+}
diff --git a/src/lib/log/root_logger_name.h b/src/lib/log/root_logger_name.h
new file mode 100644
index 0000000..80691d1
--- /dev/null
+++ b/src/lib/log/root_logger_name.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __ROOT_LOGGER_NAME_H
+#define __ROOT_LOGGER_NAME_H
+
+#include <string>
+
+/// \brief Define Name of Root Logger
+///
+/// In the log4cxx system, the root logger is ".". The definition for the
+/// BIND-10 system is that the root logger of a program has the name of the
+/// program. This (trivial) class stores the name of the program in a
+/// location accessible to the logger classes.
+
+namespace isc {
+namespace log {
+
+class RootLoggerName {
+public:
+
+ /// \brief Constructor
+ ///
+ /// Sets the root logger name. Although the name is static, setting the
+ /// name in the constructor allows static initialization of the name by
+ /// declaring an external instance of the class in the main execution unit.
+ RootLoggerName(const std::string& name) {
+ setName(name);
+ }
+
+ /// \brief Set Root Logger Name
+ ///
+ /// \param name Name of the root logger. This should be the program
+ /// name.
+ static void setName(const std::string& name) {
+ name_ = name;
+ }
+
+ /// \brief Get Root Logger Name
+ ///
+ /// \return Name of the root logger.
+ static std::string getName() {
+ return name_;
+ }
+
+private:
+ static std::string name_; ///< Name of the root logger
+};
+
+}
+}
+
+#endif // __ROOT_LOGGER_NAME_H
diff --git a/src/lib/log/strutil.cc b/src/lib/log/strutil.cc
new file mode 100644
index 0000000..4b96601
--- /dev/null
+++ b/src/lib/log/strutil.cc
@@ -0,0 +1,138 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#include <numeric>
+#include <iostream>
+
+#include <string.h>
+#include <strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace strutil {
+
+// Normalize slashes
+
+void
+normalizeSlash(std::string& name) {
+ if (!name.empty()) {
+ size_t pos = 0;
+ while ((pos = name.find('\\', pos)) != std::string::npos) {
+ name[pos] = '/';
+ }
+ }
+}
+
+// Trim String
+
+string
+trim(const string& instring) {
+ static const char* blanks = " \t\n";
+
+ string retstring = "";
+ if (!instring.empty()) {
+
+ // Search for first non-blank character in the string
+ size_t first = instring.find_first_not_of(blanks);
+ if (first != string::npos) {
+
+ // String not all blanks, so look for last character
+ size_t last = instring.find_last_not_of(blanks);
+
+ // Extract the trimmed substring
+ retstring = instring.substr(first, (last - first + 1));
+ }
+ }
+
+ return retstring;
+}
+
+// Tokenise string. As noted in the header, this is locally written to avoid
+// another dependency on a Boost library.
+
+vector<string>
+tokens(const std::string text, const std::string& delim) {
+ vector<string> result;
+
+ // Search for the first non-delimiter character
+ size_t start = text.find_first_not_of(delim);
+ while (start != string::npos) {
+
+ // Non-delimiter found, look for next delimiter
+ size_t end = text.find_first_of(delim, start);
+ if (end != string::npos) {
+
+ // Delimiter found, so extract string & search for start of next
+ // non-delimiter segment.
+ result.push_back(text.substr(start, (end - start)));
+ start = text.find_first_not_of(delim, end);
+
+ } else {
+
+ // End of string found, extract rest of string and flag to exit
+ result.push_back(text.substr(start));
+ start = string::npos;
+ }
+ }
+
+ return result;
+}
+
+// Local function to pass to accumulate() for summing up string lengths.
+
+namespace {
+
+size_t
+lengthSum(string::size_type curlen, const string& cur_string) {
+ return (curlen + cur_string.size());
+}
+
+}
+
+// Provide printf-style formatting.
+
+std::string
+format(const std::string& format, const std::vector<std::string>& args) {
+
+ static const string flag = "%s";
+
+ // Initialize return string. To speed things up, we'll reserve an
+ // appropriate amount of space - current string size, plus length of all
+ // the argument strings, less two characters for each argument (the %s in
+ // the format string is being replaced).
+ string result;
+ size_t length = accumulate(args.begin(), args.end(), format.size(),
+ lengthSum) - (args.size() * flag.size());
+ result.reserve(length);
+
+ // Iterate through replacing all tokens
+ result = format;
+ size_t tokenpos = 0; // Position of last token replaced
+ int i = 0; // Index into argument array
+
+ while ((i < args.size()) && (tokenpos != string::npos)) {
+ tokenpos = result.find(flag, tokenpos);
+ if (tokenpos != string::npos) {
+ result.replace(tokenpos, flag.size(), args[i++]);
+ }
+ }
+
+ return result;
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/strutil.h b/src/lib/log/strutil.h
new file mode 100644
index 0000000..cb0b793
--- /dev/null
+++ b/src/lib/log/strutil.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __STRUTIL_H
+#define __STRUTIL_H
+
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace strutil {
+
+/// \brief A Set of C++ Utilities for Manipulating Strings
+
+/// \brief Normalize Backslash
+///
+/// Only relevant to Windows, this replaces all "\" in a string with "/" and
+/// returns the result. On other systems it is a no-op. Note that Windows does
+/// recognise file names with the "\" replaced by "/" (at least in system calls,
+/// if not the command line).
+///
+/// \param name Name to be substituted
+void normalizeSlash(std::string& name);
+
+
+/// \brief Trim Leading and Trailing Spaces
+///
+/// Returns a copy of the input string but with any leading or trailing spaces
+/// or tabs removed.
+///
+/// \param instring Input string to modify
+///
+/// \return String with leading and trailing spaces removed
+std::string trim(const std::string& instring);
+
+
+/// \brief Split String into Tokens
+///
+/// Splits a string into tokens (the tokens being delimited by one or more of
+/// the delimiter characters) and returns the tokens in a vector array. Note
+/// that adjacent delimiters are considered to be a single delimiter.
+///
+/// Special cases are:
+/// -# The empty string is considered to be zero tokens.
+/// -# A string comprising nothing but delimiters is considered to be zero
+/// tokens.
+///
+/// The reasoning behind this is that the string can be thought of as having
+/// invisible leading and trailing delimiter characters. Therefore both cases
+/// reduce to a set of contiguous delimiters, which are considered a single
+/// delimiter (so getting rid of the string).
+///
+/// We could use Boost for this, but this (simple) function eliminates one
+/// dependency in the code.
+///
+/// \param text String to be split. Passed by value as the internal copy is
+/// altered during the processing.
+/// \param delim Delimiter characters
+///
+/// \return Vector of tokens.
+std::vector<std::string> tokens(const std::string text,
+ const std::string& delim = std::string(" \t\n"));
+
+
+/// \brief Uppercase Character
+///
+/// Used in uppercase() to pass as an argument to std::transform(). The
+/// function std::toupper() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because defererencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be upper-cased.
+///
+/// \return Uppercase version of the argument
+inline char toUpper(char chr) {
+ return static_cast<char>(std::toupper(static_cast<int>(chr)));
+}
+
+
+/// \brief Uppercase String
+///
+/// A convenience function to uppercase a string.
+///
+/// \param text String to be upper-cased.
+inline void uppercase(std::string& text) {
+ std::transform(text.begin(), text.end(), text.begin(),
+ isc::strutil::toUpper);
+}
+
+/// \brief Lowercase Character
+///
+/// Used in lowercase() to pass as an argument to std::transform(). The
+/// function std::tolower() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because defererencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be lower-cased.
+///
+/// \return Lowercase version of the argument
+inline char toLower(char chr) {
+ return static_cast<char>(std::tolower(static_cast<int>(chr)));
+}
+
+/// \brief Lowercase String
+///
+/// A convenience function to lowercase a string
+///
+/// \param text String to be lower-cased.
+inline void lowercase(std::string& text) {
+ std::transform(text.begin(), text.end(), text.begin(),
+ isc::strutil::toLower);
+}
+
+
+/// \brief Apply Formatting
+///
+/// Given a printf-style format string containing only "%s" place holders
+/// (others are ignored) and a vector of strings, this produces a single string
+/// with the placeholders replaced.
+///
+/// \param format Format string
+/// \param args Vector of argument strings
+///
+/// \return Resultant string
+std::string format(const std::string& format,
+ const std::vector<std::string>& args);
+
+
+} // namespace strutil
+} // namespace isc
+
+#endif // __STRUTIL_H
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
new file mode 100644
index 0000000..01973c9
--- /dev/null
+++ b/src/lib/log/tests/Makefile.am
@@ -0,0 +1,45 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = root_logger_name_unittest.cc
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += logger_unittest.cc
+run_unittests_SOURCES += message_dictionary_unittest.cc
+run_unittests_SOURCES += message_reader_unittest.cc
+run_unittests_SOURCES += message_initializer_unittest.cc
+run_unittests_SOURCES += message_initializer_unittest_2.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += xdebuglevel_unittest.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += -llog4cxx
+endif
+
+TESTS += logger_support_test
+logger_support_test_SOURCES = logger_support_test.cc
+logger_support_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+logger_support_test_LDFLAGS = $(AM_LDFLAGS)
+logger_support_test_LDADD = $(top_builddir)/src/lib/log/liblog.la
+
+noinst_PROGRAMS = $(TESTS)
+
+# Additional test using the shell
+PYTESTS = run_time_init_test.sh
+check-local:
+ $(SHELL) $(abs_builddir)/run_time_init_test.sh
diff --git a/src/lib/log/tests/filename_unittest.cc b/src/lib/log/tests/filename_unittest.cc
new file mode 100644
index 0000000..c33be9f
--- /dev/null
+++ b/src/lib/log/tests/filename_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/filename.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class FilenameTest : public ::testing::Test {
+protected:
+ FilenameTest()
+ {
+ }
+};
+
+
+// Check that the name can be changed
+
+TEST_F(FilenameTest, SetName) {
+ Filename fname("/a/b/c.d");
+ EXPECT_EQ("/a/b/c.d", fname.fullName());
+
+ fname.setName("test.txt");
+ EXPECT_EQ("test.txt", fname.fullName());
+}
+
+
+// Check that the components are split correctly. This is a check of the
+// private member split() method.
+
+TEST_F(FilenameTest, Components) {
+
+ // Complete name
+ Filename fname("/alpha/beta/gamma.delta");
+ EXPECT_EQ("/alpha/beta/", fname.directory());
+ EXPECT_EQ("gamma", fname.name());
+ EXPECT_EQ(".delta", fname.extension());
+
+ // Directory only
+ fname.setName("/gamma/delta/");
+ EXPECT_EQ("/gamma/delta/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Filename only
+ fname.setName("epsilon");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("epsilon", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Extension only
+ fname.setName(".zeta");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".zeta", fname.extension());
+
+ // Missing directory
+ fname.setName("eta.theta");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("eta", fname.name());
+ EXPECT_EQ(".theta", fname.extension());
+
+ // Missing filename
+ fname.setName("/iota/.kappa");
+ EXPECT_EQ("/iota/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".kappa", fname.extension());
+
+ // Missing extension
+ fname.setName("lambda/mu/nu");
+ EXPECT_EQ("lambda/mu/", fname.directory());
+ EXPECT_EQ("nu", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Check that the decomposition can occur in the presence of leading and
+ // trailing spaces
+ fname.setName(" lambda/mu/nu\t ");
+ EXPECT_EQ("lambda/mu/", fname.directory());
+ EXPECT_EQ("nu", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Empty string
+ fname.setName("");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // ... and just spaces
+ fname.setName(" ");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Check corner cases - where separators are present, but strings are
+ // absent.
+ fname.setName("/");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ fname.setName(".");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ fname.setName("/.");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ // Note that the space is a valid filename here; only leading and trailing
+ // spaces should be trimmed.
+ fname.setName("/ .");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ(" ", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ fname.setName(" / . ");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ(" ", fname.name());
+ EXPECT_EQ(".", fname.extension());
+}
+
+// Check that the expansion with a default works.
+
+TEST_F(FilenameTest, ExpandWithDefault) {
+ Filename fname("a.b");
+
+ // These tests also check that the trimming of the default component is
+ // done properly.
+ EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/ "));
+ EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
+ EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
+
+ fname.setName("/a/b/c");
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
+ EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
+
+ fname.setName(".h");
+ EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
+}
+
+// Check that we can use this as a default in expanding a filename
+
+TEST_F(FilenameTest, UseAsDefault) {
+
+ Filename fname("a.b");
+
+ // These tests also check that the trimming of the default component is
+ // done properly.
+ EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/ "));
+ EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
+ EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
+
+ fname.setName("/a/b/c");
+ EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
+ EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
+ EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
+ EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
+ EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
+}
diff --git a/src/lib/log/tests/localdef.mes b/src/lib/log/tests/localdef.mes
new file mode 100644
index 0000000..98e197d
--- /dev/null
+++ b/src/lib/log/tests/localdef.mes
@@ -0,0 +1,23 @@
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# \brief Local Definitions
+#
+# Holds local definitions of some of the messages produced by the program
+# logger_support_test, and is used as input to check that run-time message
+# replacement works.
+
+NOTHERE this message is not in the global dictionary
+READERR replacement read error, parameters: '%s' and '%s'
+UNRECDIR replacement unrecognised directive message, parameter is '%s'
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
new file mode 100644
index 0000000..acca4f6
--- /dev/null
+++ b/src/lib/log/tests/logger_support_test.cc
@@ -0,0 +1,109 @@
+// Copyright (C) 2010 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.
+
+// $Id: $
+
+/// \brief Example Program
+///
+/// Simple example program showing how to use the logger.
+
+#include <unistd.h>
+#include <string.h>
+
+#include <iostream>
+
+#include <log/logger.h>
+#include <log/logger_support.h>
+#include <log/root_logger_name.h>
+
+// Include a set of message definitions.
+#include <log/messagedef.h>
+
+using namespace isc::log;
+
+// Declare root logger and a logger to use an example.
+//RootLoggerName root_name("testing");
+
+RootLoggerName root("alpha");
+Logger logger_ex("example");
+Logger logger_dlm("dlm");
+
+// The program is invoked:
+//
+// logger_support_test [-s severity] [-d level ] [local_file]
+//
+// "severity" is one of "debug", "info", "warn", "error", "fatal"
+// "level" is the debug level, a number between 0 and 99
+// "local_file" is the name of a local file.
+//
+// The program sets the attributes on the root logger. Looking
+// at the output determines whether the program worked.e root logger. Looking
+// at the output determines whether the
+
+int main(int argc, char** argv) {
+
+ Logger::Severity severity = Logger::INFO;
+ int dbglevel = -1;
+ const char* localfile = NULL;
+ int option;
+
+ // Parse options
+ while ((option = getopt(argc, argv, "s:d:")) != -1) {
+ switch (option) {
+ case 's':
+ if (strcmp(optarg, "debug") == 0) {
+ severity = Logger::DEBUG;
+ } else if (strcmp(optarg, "info") == 0) {
+ severity = Logger::INFO;
+ } else if (strcmp(optarg, "warn") == 0) {
+ severity = Logger::WARN;
+ } else if (strcmp(optarg, "error") == 0) {
+ severity = Logger::ERROR;
+ } else if (strcmp(optarg, "fatal") == 0) {
+ severity = Logger::FATAL;
+ } else {
+ std::cout << "Unrecognised severity option: " <<
+ optarg << "\n";
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ dbglevel = atoi(optarg);
+ break;
+
+ default:
+ std::cout << "Unrecognised option: " <<
+ static_cast<char>(option) << "\n";
+ }
+ }
+
+ if (optind < argc) {
+ localfile = argv[optind];
+ }
+
+ // Update the logging parameters
+ runTimeInit(severity, dbglevel, localfile);
+
+ // Log a few messages
+ logger_ex.fatal(MSG_WRITERR, "test1", "42");
+ logger_ex.error(MSG_UNRECDIR, "false");
+ logger_dlm.warn(MSG_READERR, "a.txt", "dummy test");
+ logger_dlm.info(MSG_OPENIN, "example.msg", "dummy test");
+ logger_ex.debug(0, MSG_UNRECDIR, "[abc]");
+ logger_ex.debug(24, MSG_UNRECDIR, "[24]");
+ logger_ex.debug(25, MSG_UNRECDIR, "[25]");
+ logger_ex.debug(26, MSG_UNRECDIR, "[26]");
+ return 0;
+}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
new file mode 100644
index 0000000..e15ec42
--- /dev/null
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -0,0 +1,395 @@
+// Copyright (C) 2010 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.
+
+// $Id: $
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/root_logger_name.h>
+#include <log/logger.h>
+#include <log/messagedef.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+namespace isc {
+namespace log {
+
+/// \brief Test Logger
+///
+/// This logger is a subclass of the logger class under test, but makes
+/// protected methods public (for testing)
+
+class TestLogger : public Logger {
+public:
+ /// \brief constructor
+ TestLogger(const string& name) : Logger(name, true)
+ {}
+
+ /// \brief Logger Equality
+ bool operator==(const TestLogger& other) {
+ return Logger::operator==(other);
+ }
+
+ /// \brief Logger is Null
+ bool isInitialized() const {
+ return Logger::isInitialized();
+ }
+
+ /// \brief Conversion Between log4cxx Number and BIND-10 Severity
+ Severity convertLevel(int value) {
+ return Logger::convertLevel(value);
+ }
+};
+
+} // namespace log
+} // namespace isc
+
+
+class LoggerTest : public ::testing::Test {
+protected:
+ LoggerTest()
+ {
+ }
+};
+
+
+// Checks that the logger is named correctly.
+
+TEST_F(LoggerTest, Name) {
+
+ // Create a logger
+ RootLoggerName::setName("test1");
+ Logger logger("alpha");
+
+ // ... and check the name
+ EXPECT_EQ(string("test1.alpha"), logger.getName());
+}
+
+// This test attempts to get two instances of a logger with the same name
+// and checks that they are in fact the same logger.
+
+TEST_F(LoggerTest, GetLogger) {
+
+ // Set the root logger name (not strictly needed, but this will be the
+ // case in the program(.
+ RootLoggerName::setName("test2");
+
+ const string name1 = "alpha";
+ const string name2 = "beta";
+
+ // Instantiate two loggers that should be the same
+ TestLogger logger1(name1);
+ TestLogger logger2(name1);
+
+ // And check they are null at this point.
+ EXPECT_FALSE(logger1.isInitialized());
+ EXPECT_FALSE(logger2.isInitialized());
+
+ // Do some random operation
+ EXPECT_TRUE(logger1.isFatalEnabled());
+ EXPECT_TRUE(logger2.isFatalEnabled());
+
+ // And check they initialized and equal
+ EXPECT_TRUE(logger1.isInitialized());
+ EXPECT_TRUE(logger2.isInitialized());
+ EXPECT_TRUE(logger1 == logger2);
+
+ // Instantiate another logger with another name and check that it
+ // is different to the previously instantiated ones.
+ TestLogger logger3(name2);
+ EXPECT_FALSE(logger3.isInitialized());
+ EXPECT_TRUE(logger3.isFatalEnabled());
+ EXPECT_TRUE(logger3.isInitialized());
+ EXPECT_FALSE(logger1 == logger3);
+}
+
+// Test the number to severity conversion function
+
+TEST_F(LoggerTest, ConvertLevel) {
+
+ // Create a logger
+ RootLoggerName::setName("test3");
+ TestLogger logger("alpha");
+
+ // Basic 1:1
+ EXPECT_EQ(Logger::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
+ EXPECT_EQ(Logger::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
+ EXPECT_EQ(Logger::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
+ EXPECT_EQ(Logger::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
+ EXPECT_EQ(Logger::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
+ EXPECT_EQ(Logger::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
+ EXPECT_EQ(Logger::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
+ EXPECT_EQ(Logger::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
+
+ // Now some debug levels
+ EXPECT_EQ(Logger::DEBUG,
+ logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
+ EXPECT_EQ(Logger::DEBUG,
+ logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
+ EXPECT_EQ(Logger::DEBUG,
+ logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
+}
+
+// Check that the logger levels are get set properly.
+
+TEST_F(LoggerTest, Severity) {
+
+ // Create a logger
+ RootLoggerName::setName("test3");
+ TestLogger logger("alpha");
+
+ // Now check the levels
+ logger.setSeverity(Logger::NONE);
+ EXPECT_EQ(Logger::NONE, logger.getSeverity());
+
+ logger.setSeverity(Logger::FATAL);
+ EXPECT_EQ(Logger::FATAL, logger.getSeverity());
+
+ logger.setSeverity(Logger::ERROR);
+ EXPECT_EQ(Logger::ERROR, logger.getSeverity());
+
+ logger.setSeverity(Logger::WARN);
+ EXPECT_EQ(Logger::WARN, logger.getSeverity());
+
+ logger.setSeverity(Logger::INFO);
+ EXPECT_EQ(Logger::INFO, logger.getSeverity());
+
+ logger.setSeverity(Logger::DEBUG);
+ EXPECT_EQ(Logger::DEBUG, logger.getSeverity());
+
+ logger.setSeverity(Logger::DEFAULT);
+ EXPECT_EQ(Logger::DEFAULT, logger.getSeverity());
+}
+
+// Check that the debug level is set correctly.
+
+TEST_F(LoggerTest, DebugLevels) {
+
+ // Create a logger
+ RootLoggerName::setName("test4");
+ TestLogger logger("alpha");
+
+ // Debug level should be 0 if not at debug severity
+ logger.setSeverity(Logger::NONE, 20);
+ EXPECT_EQ(0, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::INFO, 42);
+ EXPECT_EQ(0, logger.getDebugLevel());
+
+ // Should be the value set if the severity is set to DEBUG though.
+ logger.setSeverity(Logger::DEBUG, 32);
+ EXPECT_EQ(32, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::DEBUG, 97);
+ EXPECT_EQ(97, logger.getDebugLevel());
+
+ // Try the limits
+ logger.setSeverity(Logger::DEBUG, -1);
+ EXPECT_EQ(0, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::DEBUG, 0);
+ EXPECT_EQ(0, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::DEBUG, 1);
+ EXPECT_EQ(1, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::DEBUG, 98);
+ EXPECT_EQ(98, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::DEBUG, 99);
+ EXPECT_EQ(99, logger.getDebugLevel());
+
+ logger.setSeverity(Logger::DEBUG, 100);
+ EXPECT_EQ(99, logger.getDebugLevel());
+}
+
+// Check that changing the parent and child severity does not affect the
+// other.
+
+TEST_F(LoggerTest, SeverityInheritance) {
+
+ // Create to loggers. We cheat here as we know that the underlying
+ // implementation (in this case log4cxx) will set a parent-child
+ // relationship if the loggers are named <parent> and <parent>.<child>.
+
+ RootLoggerName::setName("test5");
+ TestLogger parent("alpha");
+ TestLogger child("alpha.beta");
+
+ // By default, newly created loggers should have a level of DEFAULT
+ // (i.e. default to parent)
+ EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
+ EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+
+ // Set the severity of the child to something other than the default -
+ // check it changes and that of the parent does not.
+ child.setSeverity(Logger::INFO);
+ EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
+ EXPECT_EQ(Logger::INFO, child.getSeverity());
+
+ // Reset the child severity and set that of the parent
+ child.setSeverity(Logger::DEFAULT);
+ EXPECT_EQ(Logger::DEFAULT, parent.getSeverity());
+ EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+ parent.setSeverity(Logger::WARN);
+ EXPECT_EQ(Logger::WARN, parent.getSeverity());
+ EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+}
+
+// Check that severity is inherited.
+
+TEST_F(LoggerTest, EffectiveSeverityInheritance) {
+
+ // Create to loggers. We cheat here as we know that the underlying
+ // implementation (in this case log4cxx) will set a parent-child
+ // relationship if the loggers are named <parent> and <parent>.<child>.
+
+ RootLoggerName::setName("test6");
+ Logger parent("test6");
+ Logger child("test6.beta");
+
+ // By default, newly created loggers should have a level of DEFAULT
+ // (i.e. default to parent) and the root should have a default severity
+ // of INFO. However, the latter is only enforced when created by the
+ // RootLogger class, so explicitly set it for the parent for now.
+ parent.setSeverity(Logger::INFO);
+ EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+
+ EXPECT_EQ(Logger::DEFAULT, child.getSeverity());
+ EXPECT_EQ(Logger::INFO, child.getEffectiveSeverity());
+
+ // Set the severity of the child to something other than the default -
+ // check it changes and that of the parent does not.
+ child.setSeverity(Logger::FATAL);
+ EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+ EXPECT_EQ(Logger::FATAL, child.getEffectiveSeverity());
+
+ // Reset the child severity and check again.
+ child.setSeverity(Logger::DEFAULT);
+ EXPECT_EQ(Logger::INFO, parent.getEffectiveSeverity());
+ EXPECT_EQ(Logger::INFO, child.getEffectiveSeverity());
+
+ // Change the parwnt's severity and check it is reflects in the child.
+ parent.setSeverity(Logger::WARN);
+ EXPECT_EQ(Logger::WARN, parent.getEffectiveSeverity());
+ EXPECT_EQ(Logger::WARN, child.getEffectiveSeverity());
+}
+
+// Test the isXxxxEnabled methods.
+
+TEST_F(LoggerTest, IsXxxEnabled) {
+
+ RootLoggerName::setName("test7");
+ Logger logger("test7");
+
+ logger.setSeverity(Logger::INFO);
+ EXPECT_FALSE(logger.isDebugEnabled());
+ EXPECT_TRUE(logger.isInfoEnabled());
+ EXPECT_TRUE(logger.isWarnEnabled());
+ EXPECT_TRUE(logger.isErrorEnabled());
+ EXPECT_TRUE(logger.isFatalEnabled());
+
+ logger.setSeverity(Logger::WARN);
+ EXPECT_FALSE(logger.isDebugEnabled());
+ EXPECT_FALSE(logger.isInfoEnabled());
+ EXPECT_TRUE(logger.isWarnEnabled());
+ EXPECT_TRUE(logger.isErrorEnabled());
+ EXPECT_TRUE(logger.isFatalEnabled());
+
+ logger.setSeverity(Logger::ERROR);
+ EXPECT_FALSE(logger.isDebugEnabled());
+ EXPECT_FALSE(logger.isInfoEnabled());
+ EXPECT_FALSE(logger.isWarnEnabled());
+ EXPECT_TRUE(logger.isErrorEnabled());
+ EXPECT_TRUE(logger.isFatalEnabled());
+
+ logger.setSeverity(Logger::FATAL);
+ EXPECT_FALSE(logger.isDebugEnabled());
+ EXPECT_FALSE(logger.isInfoEnabled());
+ EXPECT_FALSE(logger.isWarnEnabled());
+ EXPECT_FALSE(logger.isErrorEnabled());
+ EXPECT_TRUE(logger.isFatalEnabled());
+
+ // Check various debug levels
+
+ logger.setSeverity(Logger::DEBUG);
+ EXPECT_TRUE(logger.isDebugEnabled());
+ EXPECT_TRUE(logger.isInfoEnabled());
+ EXPECT_TRUE(logger.isWarnEnabled());
+ EXPECT_TRUE(logger.isErrorEnabled());
+ EXPECT_TRUE(logger.isFatalEnabled());
+
+ logger.setSeverity(Logger::DEBUG, 45);
+ EXPECT_TRUE(logger.isDebugEnabled());
+ EXPECT_TRUE(logger.isInfoEnabled());
+ EXPECT_TRUE(logger.isWarnEnabled());
+ EXPECT_TRUE(logger.isErrorEnabled());
+ EXPECT_TRUE(logger.isFatalEnabled());
+
+ // Create a child logger with no severity set, and check that it reflects
+ // the severity of the parent logger.
+
+ Logger child("test7.child");
+ logger.setSeverity(Logger::FATAL);
+ EXPECT_FALSE(child.isDebugEnabled());
+ EXPECT_FALSE(child.isInfoEnabled());
+ EXPECT_FALSE(child.isWarnEnabled());
+ EXPECT_FALSE(child.isErrorEnabled());
+ EXPECT_TRUE(child.isFatalEnabled());
+
+ logger.setSeverity(Logger::INFO);
+ EXPECT_FALSE(child.isDebugEnabled());
+ EXPECT_TRUE(child.isInfoEnabled());
+ EXPECT_TRUE(child.isWarnEnabled());
+ EXPECT_TRUE(child.isErrorEnabled());
+ EXPECT_TRUE(child.isFatalEnabled());
+}
+
+// Within the Debug level there are 100 debug levels. Test that we know
+// when to issue a debug message.
+
+TEST_F(LoggerTest, IsDebugEnabledLevel) {
+
+ RootLoggerName::setName("test8");
+ Logger logger("test8");
+
+ int MID_LEVEL = (MIN_DEBUG_LEVEL + MAX_DEBUG_LEVEL) / 2;
+
+ logger.setSeverity(Logger::DEBUG);
+ EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+ EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL));
+ EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+
+ logger.setSeverity(Logger::DEBUG, MIN_DEBUG_LEVEL);
+ EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+ EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL));
+ EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+
+ logger.setSeverity(Logger::DEBUG, MID_LEVEL);
+ EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+ EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL - 1));
+ EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
+ EXPECT_FALSE(logger.isDebugEnabled(MID_LEVEL + 1));
+ EXPECT_FALSE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+
+ logger.setSeverity(Logger::DEBUG, MAX_DEBUG_LEVEL);
+ EXPECT_TRUE(logger.isDebugEnabled(MIN_DEBUG_LEVEL));
+ EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
+ EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
+}
diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc
new file mode 100644
index 0000000..78aa851
--- /dev/null
+++ b/src/lib/log/tests/message_dictionary_unittest.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <cstddef>
+#include <string>
+#include <gtest/gtest.h>
+#include <log/message_dictionary.h>
+#include <log/message_types.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class MessageDictionaryTest : public ::testing::Test {
+protected:
+ MessageDictionaryTest() :
+ alpha_id("ALPHA"), alpha_text("This is alpha"),
+ beta_id("BETA"), beta_text("This is beta"),
+ gamma_id("GAMMA"), gamma_text("This is gamma")
+ {
+ }
+
+ MessageID alpha_id;
+ std::string alpha_text;
+ MessageID beta_id;
+ std::string beta_text;
+ MessageID gamma_id;
+ std::string gamma_text;
+
+};
+
+
+// Check that the global dictionary is a singleton.
+
+TEST_F(MessageDictionaryTest, GlobalTest) {
+ MessageDictionary* global = MessageDictionary::globalDictionary();
+ EXPECT_FALSE(NULL == global);
+
+ MessageDictionary* global2 = MessageDictionary::globalDictionary();
+ EXPECT_EQ(global2, global);
+}
+
+// Check that adding messages works
+
+TEST_F(MessageDictionaryTest, Add) {
+ MessageDictionary dictionary;
+ EXPECT_EQ(0, dictionary.size());
+
+ // Add a few messages and check that we can look them up and that there is
+ // nothing in the overflow vector.
+ EXPECT_TRUE(dictionary.add(alpha_id, alpha_text));
+ EXPECT_TRUE(dictionary.add(beta_id, beta_text));
+ EXPECT_EQ(2, dictionary.size());
+
+ EXPECT_EQ(alpha_text, dictionary.getText(alpha_id));
+ EXPECT_EQ(beta_text, dictionary.getText(beta_id));
+ EXPECT_EQ(string(""), dictionary.getText(gamma_id));
+
+ // Try adding a duplicate with different text. It should not replace the
+ // current text and the ID should be in the overflow section.
+ EXPECT_FALSE(dictionary.add(alpha_id, gamma_text));
+ EXPECT_EQ(2, dictionary.size());
+}
+
+// Check that replacing messages works.
+
+TEST_F(MessageDictionaryTest, Replace) {
+ MessageDictionary dictionary;
+ EXPECT_EQ(0, dictionary.size());
+
+ // Try to replace a non-existent message
+ EXPECT_FALSE(dictionary.replace(alpha_id, alpha_text));
+ EXPECT_EQ(0, dictionary.size());
+
+ // Add a couple of messages.
+ EXPECT_TRUE(dictionary.add(alpha_id, alpha_text));
+ EXPECT_TRUE(dictionary.add(beta_id, beta_text));
+ EXPECT_EQ(2, dictionary.size());
+
+ // Replace an existing message
+ EXPECT_TRUE(dictionary.replace(alpha_id, gamma_text));
+ EXPECT_EQ(2, dictionary.size());
+ EXPECT_EQ(gamma_text, dictionary.getText(alpha_id));
+
+ // ... and replace non-existent message (but now the dictionary has some
+ // items in it).
+ EXPECT_FALSE(dictionary.replace(gamma_id, alpha_text));
+ EXPECT_EQ(2, dictionary.size());
+ EXPECT_EQ(string(""), dictionary.getText(gamma_id));
+}
+
+// Load test
+
+TEST_F(MessageDictionaryTest, LoadTest) {
+ static const char* data1[] = {
+ "ALPHA", "This is alpha",
+ "BETA", "This is beta",
+ "GAMMA", "This is gamma",
+ NULL
+ };
+
+ static const char* data2[] = {
+ "DELTA", "This is delta",
+ "EPSILON", "This is epsilon",
+ "ETA", NULL
+ };
+
+ MessageDictionary dictionary1;
+ EXPECT_EQ(0, dictionary1.size());
+
+ // Load a dictionary1.
+ vector<MessageID> duplicates = dictionary1.load(data1);
+ EXPECT_EQ(3, dictionary1.size());
+ EXPECT_EQ(string(data1[1]), dictionary1.getText(data1[0]));
+ EXPECT_EQ(string(data1[3]), dictionary1.getText(data1[2]));
+ EXPECT_EQ(string(data1[5]), dictionary1.getText(data1[4]));
+ EXPECT_EQ(0, duplicates.size());
+
+ // Attempt an overwrite
+ duplicates = dictionary1.load(data1);
+ EXPECT_EQ(3, dictionary1.size());
+ EXPECT_EQ(3, duplicates.size());
+
+ // Try a new dictionary but with an incorrect number of elements
+ MessageDictionary dictionary2;
+ EXPECT_EQ(0, dictionary2.size());
+
+ duplicates = dictionary2.load(data2);
+ EXPECT_EQ(2, dictionary2.size());
+ EXPECT_EQ(string(data2[1]), dictionary2.getText(data2[0]));
+ EXPECT_EQ(string(data2[3]), dictionary2.getText(data2[2]));
+ EXPECT_EQ(string(""), dictionary2.getText(data2[4]));
+ EXPECT_EQ(0, duplicates.size());
+}
+
+// Check for some non-existent items
+
+TEST_F(MessageDictionaryTest, Lookups) {
+ static const char* data[] = {
+ "ALPHA", "This is alpha",
+ "BETA", "This is beta",
+ "GAMMA", "This is gamma",
+ NULL
+ };
+
+ MessageDictionary dictionary;
+ vector<MessageID> duplicates = dictionary.load(data);
+ EXPECT_EQ(3, dictionary.size());
+ EXPECT_EQ(0, duplicates.size());
+
+ // Valid lookups
+ EXPECT_EQ(string("This is alpha"), dictionary.getText("ALPHA"));
+ EXPECT_EQ(string("This is beta"), dictionary.getText("BETA"));
+ EXPECT_EQ(string("This is gamma"), dictionary.getText("GAMMA"));
+
+ // ... and invalid ones
+ EXPECT_EQ(string(""), dictionary.getText("XYZZY"));
+ EXPECT_EQ(string(""), dictionary.getText(""));
+ EXPECT_EQ(string(""), dictionary.getText("\n\n\n"));
+}
diff --git a/src/lib/log/tests/message_initializer_unittest.cc b/src/lib/log/tests/message_initializer_unittest.cc
new file mode 100644
index 0000000..6a1019f
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <cstddef>
+#include <string>
+#include <gtest/gtest.h>
+#include <log/message_dictionary.h>
+#include <log/message_initializer.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+const char* values1[] = {
+ "GLOBAL1", "global message one",
+ "GLOBAL2", "global message two",
+ NULL
+};
+
+const char* values2[] = {
+ "GLOBAL3", "global message three",
+ "GLOBAL4", "global message four",
+ NULL
+};
+
+}
+
+// Statically initialize the global dictionary with those messages. Three sets
+// are used to check that the declaration of separate initializer objects really// does combine the messages. (The third set is declared in the separately-
+// compiled file message_identifier_initializer_unittest_2.cc.)
+
+MessageInitializer init_message_initializer_unittest_1(values1);
+MessageInitializer init_message_initializer_unittest_2(values2);
+
+
+class MessageInitializerTest : public ::testing::Test {
+protected:
+ MessageInitializerTest()
+ {
+ }
+};
+
+
+// Check that the global dictionary is initialized with the specified
+// messages.
+
+TEST_F(MessageInitializerTest, MessageTest) {
+ MessageDictionary* global = MessageDictionary::globalDictionary();
+
+ EXPECT_EQ(string("global message one"), global->getText("GLOBAL1"));
+ EXPECT_EQ(string("global message two"), global->getText("GLOBAL2"));
+ EXPECT_EQ(string("global message three"), global->getText("GLOBAL3"));
+ EXPECT_EQ(string("global message four"), global->getText("GLOBAL4"));
+ EXPECT_EQ(string("global message five"), global->getText("GLOBAL5"));
+ EXPECT_EQ(string("global message six"), global->getText("GLOBAL6"));
+}
diff --git a/src/lib/log/tests/message_initializer_unittest_2.cc b/src/lib/log/tests/message_initializer_unittest_2.cc
new file mode 100644
index 0000000..c005033
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_unittest_2.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+// The sole purpose of this file is to provide a set of message definitions
+// in a separate compilation unit from the one in which their presence is
+// checked. This tests that merely declaring the MessageInitializer object
+// is enough to include the definitions in the global dictionary.
+
+#include <log/message_initializer.h>
+
+using namespace isc::log;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+
+const char* values3[] = {
+ "GLOBAL5", "global message five",
+ "GLOBAL6", "global message six",
+ NULL
+};
+
+}
+
+// Statically initialize the global dictionary with those messages.
+// Three sets are used to check that the declaration of separate
+// initializer objects really does combine the messages.
+MessageInitializer init_message_initializer_unittest_3(values3);
diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc
new file mode 100644
index 0000000..2891805
--- /dev/null
+++ b/src/lib/log/tests/message_reader_unittest.cc
@@ -0,0 +1,228 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <algorithm>
+#include <string>
+#include <gtest/gtest.h>
+
+#include <log/messagedef.h>
+#include <log/message_dictionary.h>
+#include <log/message_exception.h>
+#include <log/message_reader.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class MessageReaderTest : public ::testing::Test {
+protected:
+ MessageReaderTest() : dictionary_(), reader_()
+ {
+ dictionary_ = new MessageDictionary();
+ reader_.setDictionary(dictionary_);
+ }
+
+ ~MessageReaderTest() {
+ delete dictionary_;
+ }
+
+ MessageDictionary* dictionary_; // Dictionary to add messages to
+ MessageReader reader_; // Default reader object
+};
+
+
+// Check the get/set dictionary calls (using a local reader and dictionary).
+
+TEST_F(MessageReaderTest, GetSetDictionary) {
+ MessageReader reader;
+ EXPECT_TRUE(reader.getDictionary() == NULL);
+
+ MessageDictionary dictionary;
+ reader.setDictionary(&dictionary);
+ EXPECT_EQ(&dictionary, reader.getDictionary());
+}
+
+// Check for parsing blank lines and comments. These should not add to the
+// dictionary and each parse should return success.
+
+TEST_F(MessageReaderTest, BlanksAndComments) {
+
+ // Ensure that the dictionary is empty.
+ EXPECT_EQ(0, dictionary_->size());
+
+ // Add a number of blank lines and comments and check that (a) they are
+ // parsed successfully ...
+ EXPECT_NO_THROW(reader_.processLine(""));
+ EXPECT_NO_THROW(reader_.processLine(" "));
+ EXPECT_NO_THROW(reader_.processLine(" \n "));
+ EXPECT_NO_THROW(reader_.processLine("# This is a comment"));
+ EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment"));
+ EXPECT_NO_THROW(reader_.processLine(" + A description line"));
+ EXPECT_NO_THROW(reader_.processLine("#+ A comment"));
+ EXPECT_NO_THROW(reader_.processLine(" +# A description line"));
+
+ // ... and (b) nothing gets added to either the map or the not-added section.
+ EXPECT_EQ(0, dictionary_->size());
+ vector<MessageID> not_added = reader_.getNotAdded();
+ EXPECT_EQ(0, not_added.size());
+}
+
+
+// Local test to check that processLine generates the right exception.
+
+void
+processLineException(MessageReader& reader, const char* what,
+ MessageID& expected) {
+
+ try {
+ reader.processLine(what);
+ FAIL() << "MessageReader::processLine() should throw an exception " <<
+ " with message ID " << expected << " for '" << what << "'\n";
+ } catch (MessageException& e) {
+ EXPECT_EQ(expected, e.id());
+ } catch (...) {
+ FAIL() << "Unknown exception thrown by MessageReader::processLine()\n";
+ }
+}
+
+// Check that it can parse a prefix
+
+TEST_F(MessageReaderTest, Prefix) {
+
+ // Check that no prefix is present
+ EXPECT_EQ(string(""), reader_.getPrefix());
+
+ // Check that a prefix directive with no argument generates an error.
+ processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
+
+ // Check a prefix with multiple arguments is invalid
+ processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
+
+ // Prefixes should be alphanumeric (with underscores) and not start
+ // with a number.
+ processLineException(reader_, "$prefix ab[cd", MSG_PRFINVARG);
+ processLineException(reader_, "$prefix 123", MSG_PRFINVARG);
+ processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
+
+ // A valid prefix should be accepted
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__"));
+ EXPECT_EQ(string("DLM__"), reader_.getPrefix());
+
+ // And check that the parser fails on invalid prefixes...
+ processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
+
+ // ... and rejects another valid one
+ processLineException(reader_, "$PREFIX ABC", MSG_DUPLPRFX);
+
+ // Check that we can clear the prefix as well
+ reader_.clearPrefix();
+ EXPECT_EQ(string(""), reader_.getPrefix());
+}
+
+// Check that it can parse a line
+
+TEST_F(MessageReaderTest, ValidMessageAddDefault) {
+
+ // Add a couple of valid messages
+ reader_.processLine("GLOBAL1\t\tthis is message global one\n");
+ reader_.processLine("GLOBAL2 this is message global two");
+
+ // ... and check them
+ EXPECT_EQ(string("this is message global one"),
+ dictionary_->getText("GLOBAL1"));
+ EXPECT_EQ(string("this is message global two"),
+ dictionary_->getText("GLOBAL2"));
+ EXPECT_EQ(2, dictionary_->size());
+
+ // ... and ensure no messages were not added
+ vector<MessageID> not_added = reader_.getNotAdded();
+ EXPECT_EQ(0, not_added.size());
+}
+
+TEST_F(MessageReaderTest, ValidMessageAdd) {
+
+ // Add a couple of valid messages
+ reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+ MessageReader::ADD);
+ reader_.processLine("GLOBAL2 this is message global two",
+ MessageReader::ADD);
+
+ // ... and check them
+ EXPECT_EQ(string("this is message global one"),
+ dictionary_->getText("GLOBAL1"));
+ EXPECT_EQ(string("this is message global two"),
+ dictionary_->getText("GLOBAL2"));
+ EXPECT_EQ(2, dictionary_->size());
+
+ // ... and ensure no messages were not added
+ vector<MessageID> not_added = reader_.getNotAdded();
+ EXPECT_EQ(0, not_added.size());
+}
+
+TEST_F(MessageReaderTest, ValidMessageReplace) {
+
+ dictionary_->add("GLOBAL1", "original global1 message");
+ dictionary_->add("GLOBAL2", "original global2 message");
+
+ // Replace a couple of valid messages
+ reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+ MessageReader::REPLACE);
+ reader_.processLine("GLOBAL2 this is message global two",
+ MessageReader::REPLACE);
+
+ // ... and check them
+ EXPECT_EQ(string("this is message global one"),
+ dictionary_->getText("GLOBAL1"));
+ EXPECT_EQ(string("this is message global two"),
+ dictionary_->getText("GLOBAL2"));
+ EXPECT_EQ(2, dictionary_->size());
+
+ // ... and ensure no messages were not added
+ vector<MessageID> not_added = reader_.getNotAdded();
+ EXPECT_EQ(0, not_added.size());
+}
+
+// Do checks on overflows, although this essentially duplicates the checks
+// in MessageDictionary.
+
+TEST_F(MessageReaderTest, Overflows) {
+
+ // Add a couple of valid messages
+ reader_.processLine("GLOBAL1\t\tthis is message global one\n");
+ reader_.processLine("GLOBAL2 this is message global two");
+
+ // Add a duplicate in ADD mode.
+ reader_.processLine("GLOBAL1\t\tthis is a replacement for global one");
+
+ // Replace a non-existent one in REPLACE mode
+ reader_.processLine("LOCAL\t\tthis is a new message",
+ MessageReader::REPLACE);
+
+ // Check what is in the dictionary.
+ EXPECT_EQ(string("this is message global one"),
+ dictionary_->getText("GLOBAL1"));
+ EXPECT_EQ(string("this is message global two"),
+ dictionary_->getText("GLOBAL2"));
+ EXPECT_EQ(2, dictionary_->size());
+
+ // ... and ensure no overflows
+ vector<MessageID> not_added = reader_.getNotAdded();
+ ASSERT_EQ(2, not_added.size());
+
+ sort(not_added.begin(), not_added.end());
+ EXPECT_EQ(string("GLOBAL1"), not_added[0]);
+ EXPECT_EQ(string("LOCAL"), not_added[1]);
+}
diff --git a/src/lib/log/tests/root_logger_name_unittest.cc b/src/lib/log/tests/root_logger_name_unittest.cc
new file mode 100644
index 0000000..6994dc6
--- /dev/null
+++ b/src/lib/log/tests/root_logger_name_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/root_logger_name.h>
+
+using namespace isc;
+using namespace isc::log;
+
+class RootLoggerNameTest : public ::testing::Test {
+protected:
+ RootLoggerNameTest()
+ {
+ }
+};
+
+// Check of the (only) functionality of the class.
+
+TEST_F(RootLoggerNameTest, SetGet) {
+ const std::string name1 = "test1";
+ const std::string name2 = "test2";
+
+ // Check that Set/Get works
+ RootLoggerName::setName(name1);
+ EXPECT_EQ(name1, RootLoggerName::getName());
+
+ // We could not test that the root logger name is initialised
+ // correctly (as there is one instance of it and we don't know
+ // when this test will be run) so to check that setName() actually
+ // does change the name, run the test again with a different name.
+ //
+ // (There was always the outside chance that the root logger name
+ // was initialised with name1 and that setName() has no effect.)
+ RootLoggerName::setName(name2);
+ EXPECT_EQ(name2, RootLoggerName::getName());
+}
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
new file mode 100755
index 0000000..196cba9
--- /dev/null
+++ b/src/lib/log/tests/run_time_init_test.sh.in
@@ -0,0 +1,84 @@
+#!/bin/bash
+# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id$
+
+tempfile=`echo /tmp/run_init_test_$$`
+failcount=0
+localmes=@abs_builddir@/localdef.mes
+tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
+
+function passfail() {
+ if [ $1 -eq 0 ]; then
+ echo "pass"
+ else
+ echo "FAIL"
+ fi
+ failcount=`expr $failcount + $1`
+}
+
+echo -n "1. runInitTest default parameters: "
+cat > $tempfile << .
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+WARN [alpha.dlm] READERR, error reading from a.txt: dummy test
+INFO [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+.
+./logger_support_test | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Severity filter: "
+cat > $tempfile << .
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+.
+./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "3. Debug level: "
+cat > $tempfile << .
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+WARN [alpha.dlm] READERR, error reading from a.txt: dummy test
+INFO [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
+DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
+DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
+.
+./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "4. Local message replacement: "
+cat > $tempfile << .
+WARN [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
+FATAL [alpha.example] WRITERR, error writing to test1: 42
+ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
+WARN [alpha.dlm] READERR, replacement read error, parameters: 'a.txt' and 'dummy test'
+INFO [alpha.dlm] OPENIN, unable to open message file example.msg for input: dummy test
+.
+./logger_support_test $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+rm -f $tempfile
+
+if [ $failcount -eq 0 ]; then
+ echo "PASS: run_time_init_test"
+elif [ $failcount -eq 1 ]; then
+ echo "FAIL: run_time_init_test - 1 test failed"
+else
+ echo "FAIL: run_time_init_test - $failcount tests failed"
+fi
+
+exit $failcount
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
new file mode 100644
index 0000000..b91ce24
--- /dev/null
+++ b/src/lib/log/tests/run_unittests.cc
@@ -0,0 +1,23 @@
+// Copyright (C) 2009 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.
+
+// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return (RUN_ALL_TESTS());
+}
diff --git a/src/lib/log/tests/strutil_unittest.cc b/src/lib/log/tests/strutil_unittest.cc
new file mode 100644
index 0000000..6e657f3
--- /dev/null
+++ b/src/lib/log/tests/strutil_unittest.cc
@@ -0,0 +1,216 @@
+// Copyright (C) 2010 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.
+
+// $Id: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log/strutil.h>
+
+using namespace isc;
+using namespace std;
+
+class StringUtilTest : public ::testing::Test {
+protected:
+ StringUtilTest()
+ {
+ }
+};
+
+
+// Check for slash replacement
+
+TEST_F(StringUtilTest, Slash) {
+
+ string instring = "";
+ isc::strutil::normalizeSlash(instring);
+ EXPECT_EQ("", instring);
+
+ instring = "C:\\A\\B\\C.D";
+ isc::strutil::normalizeSlash(instring);
+ EXPECT_EQ("C:/A/B/C.D", instring);
+
+ instring = "// \\ //";
+ isc::strutil::normalizeSlash(instring);
+ EXPECT_EQ("// / //", instring);
+}
+
+// Check that leading and trailing space trimming works
+
+TEST_F(StringUtilTest, Trim) {
+
+ // Empty and full string.
+ EXPECT_EQ("", isc::strutil::trim(""));
+ EXPECT_EQ("abcxyz", isc::strutil::trim("abcxyz"));
+
+ // Trim right-most blanks
+ EXPECT_EQ("ABC", isc::strutil::trim("ABC "));
+ EXPECT_EQ("ABC", isc::strutil::trim("ABC\t\t \n\t"));
+
+ // Left-most blank trimming
+ EXPECT_EQ("XYZ", isc::strutil::trim(" XYZ"));
+ EXPECT_EQ("XYZ", isc::strutil::trim("\t\t \tXYZ"));
+
+ // Right and left, with embedded spaces
+ EXPECT_EQ("MN \t OP", isc::strutil::trim("\t\tMN \t OP \t"));
+}
+
+// Check tokenization. Note that ASSERT_EQ is used to check the size of the
+// returned vector; if not as expected, the following references may be invalid
+// so should not be used.
+
+TEST_F(StringUtilTest, Tokens) {
+ vector<string> result;
+
+ // Default delimiters
+
+ // Degenerate cases
+ result = isc::strutil::tokens(""); // Empty string
+ EXPECT_EQ(0, result.size());
+
+ result = isc::strutil::tokens(" \n "); // String is all delimiters
+ EXPECT_EQ(0, result.size());
+
+ result = isc::strutil::tokens("abc"); // String has no delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+
+ // String containing leading and/or trailing delimiters, no embedded ones.
+ result = isc::strutil::tokens("\txyz"); // One leading delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("\t \nxyz"); // Multiple leading delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("xyz\n"); // One trailing delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("xyz \t"); // Multiple trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("\t xyz \n"); // Leading and trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ // Embedded delimiters
+ result = isc::strutil::tokens("abc\ndef"); // 2 tokens, one separator
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = isc::strutil::tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = isc::strutil::tokens("abc\n \tdef\t\tghi");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Embedded and non-embedded delimiters
+
+ result = isc::strutil::tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Non-default delimiter
+ result = isc::strutil::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+
+ // Non-default delimiters (plural)
+ result = isc::strutil::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
+ "*+-");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+}
+
+// Changing case
+
+TEST_F(StringUtilTest, ChangeCase) {
+ string mixed("abcDEFghiJKLmno123[]{=+--+]}");
+ string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
+ string lower("abcdefghijklmno123[]{=+--+]}");
+
+ string test = mixed;
+ isc::strutil::lowercase(test);
+ EXPECT_EQ(lower, test);
+
+ test = mixed;
+ isc::strutil::uppercase(test);
+ EXPECT_EQ(upper, test);
+}
+
+// Formatting
+
+TEST_F(StringUtilTest, Formatting) {
+
+ vector<string> args;
+ args.push_back("arg1");
+ args.push_back("arg2");
+ args.push_back("arg3");
+
+ string format1 = "This is a string with no tokens";
+ EXPECT_EQ(format1, isc::strutil::format(format1, args));
+
+ string format2 = ""; // Empty string
+ EXPECT_EQ(format2, isc::strutil::format(format2, args));
+
+ string format3 = " "; // Empty string
+ EXPECT_EQ(format3, isc::strutil::format(format3, args));
+
+ string format4 = "String with %d non-string tokens %lf";
+ EXPECT_EQ(format4, isc::strutil::format(format4, args));
+
+ string format5 = "String with %s correct %s number of tokens %s";
+ string result5 = "String with arg1 correct arg2 number of tokens arg3";
+ EXPECT_EQ(result5, isc::strutil::format(format5, args));
+
+ string format6 = "String with %s too %s few tokens";
+ string result6 = "String with arg1 too arg2 few tokens";
+ EXPECT_EQ(result6, isc::strutil::format(format6, args));
+
+ string format7 = "String with %s too %s many %s tokens %s !";
+ string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
+ EXPECT_EQ(result7, isc::strutil::format(format7, args));
+
+ string format8 = "String with embedded%s%s%stokens";
+ string result8 = "String with embeddedarg1arg2arg3tokens";
+ EXPECT_EQ(result8, isc::strutil::format(format8, args));
+
+ // Handle an empty vector
+ args.clear();
+ string format9 = "%s %s";
+ EXPECT_EQ(format9, isc::strutil::format(format9, args));
+}
diff --git a/src/lib/log/tests/xdebuglevel_unittest.cc b/src/lib/log/tests/xdebuglevel_unittest.cc
new file mode 100644
index 0000000..2cb0952
--- /dev/null
+++ b/src/lib/log/tests/xdebuglevel_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2010 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.
+
+// $Id: $
+
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <log4cxx/level.h>
+#include <log/xdebuglevel.h>
+#include <log/dbglevels.h>
+
+/// \brief XDebugLevel (Debug Extension to Level Class)
+///
+/// The class is an extension of the log4cxx Level class; this set of tests
+/// only test the extensions, they do not test the underlying Level class
+/// itself.
+
+using namespace log4cxx;
+
+class XDebugLevelTest : public ::testing::Test {
+protected:
+ XDebugLevelTest()
+ {
+ }
+};
+
+// Check a basic assertion about the numeric values of the debug levels
+
+TEST_F(XDebugLevelTest, NumericValues) {
+ EXPECT_EQ(XDebugLevel::XDEBUG_MIN_LEVEL_INT, Level::DEBUG_INT);
+ EXPECT_EQ(XDebugLevel::XDEBUG_MAX_LEVEL_INT,
+ Level::DEBUG_INT - MAX_DEBUG_LEVEL);
+
+ // ... and check that assumptions used below - that the debug levels
+ // range from 0 to 99 - are valid.
+ EXPECT_EQ(0, MIN_DEBUG_LEVEL);
+ EXPECT_EQ(99, MAX_DEBUG_LEVEL);
+}
+
+
+// Checks that the main function for generating logging level objects from
+// debug levels is working.
+
+TEST_F(XDebugLevelTest, GetExtendedDebug) {
+
+ // Get a debug level of 0. This should be the same as the main DEBUG
+ // level.
+ LevelPtr debug0 = XDebugLevel::getExtendedDebug(0);
+ EXPECT_EQ(std::string("DEBUG"), debug0->toString());
+ EXPECT_EQ(Level::DEBUG_INT, debug0->toInt());
+ EXPECT_TRUE(*Level::getDebug() == *debug0);
+
+ // Get an arbitrary debug level in the allowed range.
+ LevelPtr debug32 = XDebugLevel::getExtendedDebug(32);
+ EXPECT_EQ(std::string("DEBUG32"), debug32->toString());
+ EXPECT_TRUE((XDebugLevel::XDEBUG_MIN_LEVEL_INT - 32) == debug32->toInt());
+
+ // Check that a value outside the range gives the nearest level.
+ LevelPtr debug_more = XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL + 1);
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) == *debug_more);
+
+ LevelPtr debug_less = XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL - 1);
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) == *debug_less);
+}
+
+
+// Creation of a level from an int - should return the default debug level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromIntOneArg) {
+
+ // Check that a valid debug level is as expected
+ LevelPtr debug42 = XDebugLevel::toLevel(
+ XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42);
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
+
+ // ... and that an invalid one returns an object of type debug.
+ LevelPtr debug_invalid = XDebugLevel::toLevel(Level::getInfo()->toInt());
+ EXPECT_TRUE(*Level::getDebug() == *debug_invalid);
+}
+
+
+// Creation of a level from an int - should return the default level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromIntTwoArg) {
+
+ // Check that a valid debug level is as expected
+ LevelPtr debug42 = XDebugLevel::toLevel(
+ (XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42), Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
+
+ // ... and that an invalid one returns an object of type debug.
+ LevelPtr debug_invalid = XDebugLevel::toLevel(
+ Level::getInfo()->toInt(), Level::getFatal());
+ EXPECT_TRUE(*Level::getFatal() == *debug_invalid);
+}
+
+
+// Creation of a level from a string - should return the default debug level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromStringOneArg) {
+
+ // Check that a valid debug levels are as expected
+ LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
+
+ LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
+
+ LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
+
+ LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
+
+ // ... and that an invalid one returns an object of type debug (which is
+ // the equivalent of a debug level 0 object).
+ LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid1);
+
+ LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid2);
+
+ LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid3);
+
+ LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid4);
+
+ LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
+ *debug_invalid5);
+
+ LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"));
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
+ *debug_invalid6);
+}
+
+
+// Creation of a level from a string - should return the default level
+// if outside the range.
+
+TEST_F(XDebugLevelTest, FromStringTwoArg) {
+
+ // Check that a valid debug levels are as expected
+ LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"),
+ Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
+
+ LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"),
+ Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
+
+ LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"),
+ Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
+
+ LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"),
+ Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
+
+ // ... and that an invalid one returns an object of type debug (which is
+ // the equivalent of a debug level 0 object).
+ LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"),
+ Level::getFatal());
+ EXPECT_TRUE(*Level::getFatal() == *debug_invalid1);
+
+ LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"),
+ Level::getFatal());
+ EXPECT_TRUE(*Level::getFatal() == *debug_invalid2);
+
+ LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""),
+ Level::getFatal());
+ EXPECT_TRUE(*Level::getFatal() == *debug_invalid3);
+
+ LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"),
+ Level::getFatal());
+ EXPECT_TRUE(*Level::getFatal() == *debug_invalid4);
+
+ LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"),
+ Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
+ *debug_invalid5);
+
+ LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"),
+ Level::getFatal());
+ EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
+ *debug_invalid6);
+}
diff --git a/src/lib/log/xdebuglevel.cc b/src/lib/log/xdebuglevel.cc
new file mode 100644
index 0000000..7dddcff
--- /dev/null
+++ b/src/lib/log/xdebuglevel.cc
@@ -0,0 +1,148 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#include <cassert>
+#include <algorithm>
+#include <syslog.h>
+#include <string.h>
+#include <boost/lexical_cast.hpp>
+
+#include <xdebuglevel.h>
+#include <dbglevels.h>
+#include <log4cxx/helpers/stringhelper.h>
+
+using namespace log4cxx;
+using namespace log4cxx::helpers;
+
+// Storage for the logging level objects corresponding to each debug level
+
+bool XDebugLevel::dbglevels_unset_ = true;
+LevelPtr XDebugLevel::dbglevels_[NUM_DEBUG_LEVEL];
+
+// Register the class
+
+IMPLEMENT_LOG4CXX_LEVEL(XDebugLevel)
+
+
+// Create Extended Debug Level Objects
+
+LevelPtr
+XDebugLevel::getExtendedDebug(int level) {
+
+ // Initialize the logging levels corresponding to the possible range of
+ // debug if we have not already done so
+ if (dbglevels_unset_) {
+
+ // Asserting that the minimum debug level is zero - so corresponds
+ // to DEBUG_INT - means that the lowest level is set to main DEBUG
+ // level. This means that the existing logging level object can be
+ // used.
+ assert(MIN_DEBUG_LEVEL == 0);
+ dbglevels_[0] = Level::getDebug();
+
+ // Create the logging level objects for the rest of the debug levels.
+ // They are given names of the form DEBUG<debug level> (e.g. DEBUG42).
+ // They will all correspond to a syslog level of DEBUG.
+ for (int i = 1; i < NUM_DEBUG_LEVEL; ++i) {
+ std::string name = std::string("DEBUG") +
+ boost::lexical_cast<std::string>(i);
+ dbglevels_[i] = new XDebugLevel(
+ (XDebugLevel::XDEBUG_MIN_LEVEL_INT - i),
+ LOG4CXX_STR(name.c_str()), LOG_DEBUG);
+ }
+ dbglevels_unset_ = false;
+ }
+
+ // Now get the logging level object asked for. Coerce the debug level to
+ // lie in the acceptable range.
+ int actual = std::max(MIN_DEBUG_LEVEL, std::min(MAX_DEBUG_LEVEL, level));
+
+ // ... and return a pointer to the appropriate logging level object
+ return dbglevels_[actual - MIN_DEBUG_LEVEL];
+}
+
+// Convert an integer (an absolute logging level number, not a debug level) to a
+// logging level object. If it lies outside the valid range, an object
+// corresponding to the minimum debug value is returned.
+
+LevelPtr
+XDebugLevel::toLevel(int val) {
+ return toLevel(val, getExtendedDebug(MIN_DEBUG_LEVEL));
+}
+
+LevelPtr
+XDebugLevel::toLevel(int val, const LevelPtr& defaultLevel) {
+
+ // Note the reversal of the notion of MIN and MAX - see the header file for
+ // details.
+ if ((val >= XDEBUG_MAX_LEVEL_INT) && (val <= XDEBUG_MIN_LEVEL_INT)) {
+ return getExtendedDebug(XDEBUG_MIN_LEVEL_INT - val);
+ }
+ else {
+ return defaultLevel;
+ }
+}
+
+// Convert string passed to a logging level or return default level.
+
+LevelPtr
+XDebugLevel::toLevelLS(const LogString& sArg) {
+ return toLevelLS(sArg, getExtendedDebug(0));
+}
+
+LevelPtr
+XDebugLevel::toLevelLS(const LogString& sArg, const LevelPtr& defaultLevel) {
+ std::string name = sArg; // Get to known type
+ size_t length = name.size(); // Length of the string
+
+ if (length < 5) {
+
+ // String can't possibly start DEBUG so we don't know what it is.
+ return defaultLevel;
+ }
+ else {
+ if (strncasecmp(name.c_str(), "DEBUG", 5) == 0) {
+
+ // String starts "DEBUG" (or "debug" or any case mixture). The
+ // rest of the string -if any - should be a number.
+ if (length == 5) {
+
+ // It is plain "DEBUG". Take this as level 0.
+ return getExtendedDebug(0);
+ }
+ else {
+
+ // Try converting the remainder to an integer. The "5" is
+ // the length of the string "DEBUG". Note that if the number
+ // is outside the rangeof debug levels, it is coerced to the
+ // nearest limit. Thus a level of DEBUG509 will end up as
+ // if DEBUG99 has been specified.
+ try {
+ int level = boost::lexical_cast<int>(name.substr(5));
+ return getExtendedDebug(level);
+ }
+ catch (boost::bad_lexical_cast&) {
+ return defaultLevel;
+ }
+ }
+ }
+ else {
+
+ // Unknown string - return default.
+ return defaultLevel;
+ }
+ }
+}
diff --git a/src/lib/log/xdebuglevel.h b/src/lib/log/xdebuglevel.h
new file mode 100644
index 0000000..4d28ead
--- /dev/null
+++ b/src/lib/log/xdebuglevel.h
@@ -0,0 +1,164 @@
+// Copyright (C) 2010 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.
+
+// $Id$
+
+#ifndef __XDEBUGLEVEL_H
+#define __XDEBUGLEVEL_H
+
+#include <syslog.h>
+#include <log4cxx/level.h>
+
+#include <dbglevels.h>
+
+namespace log4cxx {
+
+/// \brief Debug Extension to Level Class
+///
+/// Based on the example given in the log4cxx distribution, this extends the
+/// log4cxx Level class to allow 100 debug levels.
+///
+/// First some terminology, as the use of the term "level" gets confusing. The
+/// code and comments here use the term "level" in two contexts:
+///
+/// Logging level: The category of messages to log. By default log4cxx defines
+/// the following logging levels: OFF, FATAL, ERROR, WARNING, INFO, DEBUG,
+/// TRACE, ALL. Within the context of BIND-10, OFF, TRACE and ALL are not used
+/// and the idea of DEBUG has been extended, as will be seen below.
+///
+/// Debug level: This is a number that ranges from 0 to 99 and is used by the
+/// application to control the detail of debug output. A value of 0 gives the
+/// highest-level debug output; a value of 99 gives the most verbose and most
+/// detailed. Debug messages (or whatever debug level) are only ever output
+/// when the logging level is set to DEBUG.
+///
+///
+/// With log4cxx, the various logging levels have a numeric value associated
+/// with them, such that FATAL > ERROR > WARNING etc. This suggests that the
+/// idea of debug levels can be incorporated into the existing logging level
+/// scheme by assigning them appropriate numeric values, i.e.
+///
+/// WARNING > INFO > DEBUG(0) > DEBUG(2) > ... > DEBUG(99)
+///
+/// Setting a numeric level of DEBUG enables the basic messages; setting lower
+/// numeric levels will enable progressively more messages. The lowest debug
+/// level (0) is chosen such that setting the general DEBUG logging level will
+/// automatically select that debug level.
+///
+/// This sub-class is needed because the log4cxx::Level class does not allow
+/// the setting of the numeric value of the current level to something other
+/// than the values enumerated in the class. It creates a set of log4cxx
+/// logging levels to correspond to the various debug levels. These levels have
+/// names in the range DEBUG1 to DEBUG99 (the existing Level DEBUG is used for
+/// a debug level of 0), although they are not used in BIND-10: instead the
+/// BIND-10 Logger class treats the logging levels and debug levels separately
+/// and combines them to choose the underlying log4cxx logging level.
+
+
+/// \brief Debug-Extended Level
+
+class XDebugLevel : public Level {
+ DECLARE_LOG4CXX_LEVEL(XDebugLevel)
+
+ /// Array of pointers to logging level objects, one for each debug level.
+ /// The pointer corresponding to a debug level of 0 points to the DEBUG
+ /// logging level object.
+ static LevelPtr dbglevels_[NUM_DEBUG_LEVEL];
+ static bool dbglevels_unset_;
+
+public:
+
+ // Minimum and maximum debug levels. Note that XDEBUG_MIN_LEVEL_INT is the
+ // number corresponding to the minimum debug level - and is actually larger
+ // that XDEBUG_MAX_LEVEL_INT, the number corresponding to the maximum debug
+ // level.
+ enum {
+ XDEBUG_MIN_LEVEL_INT = Level::DEBUG_INT - MIN_DEBUG_LEVEL,
+ XDEBUG_MAX_LEVEL_INT = Level::DEBUG_INT - MAX_DEBUG_LEVEL
+ };
+
+ /// \brief Constructor
+ ///
+ /// \param level Numeric value of the logging level.
+ /// \param name Name given to this logging level.
+ /// \param syslogEquivalent The category to be used by syslog when it logs
+ /// an event associated with the specified logging level.
+ XDebugLevel(int level, const LogString& name, int syslogEquivalent) :
+ Level(level, name, syslogEquivalent)
+ {}
+
+ /// \brief Create Logging Level Object
+ ///
+ /// Creates a logging level object corresponding to one of the debug levels.
+ ///
+ /// \param dbglevel The debug level, which ranges from MIN_DEBUG_LEVEL to
+ /// MAX_DEBUG_LEVEL. It is coerced to that range if it lies outside it.
+ ///
+ /// \return Pointer to the desired logging level object.
+ static LevelPtr getExtendedDebug(int dbglevel);
+
+ /// \brief Convert Integer to a Logging Level
+ ///
+ /// Returns a logging level object corresponding to the given value (which
+ /// is an absolute value of a logging level - it is not a debug level).
+ /// If the number is invalid, an object of logging level DEBUG (the
+ /// minimum debug logging level) is returned.
+ ///
+ /// \param val Number to convert to a logging level. This is an absolute
+ /// logging level number, not a debug level.
+ ///
+ /// \return Pointer to the desired logging level object.
+ static LevelPtr toLevel(int val);
+
+ /// \brief Convert Integer to a Level
+ ///
+ /// Returns a logging level object corresponding to the given value (which
+ /// is an absolute value of a logging level - it is not a debug level).
+ /// If the number is invalid, the given default is returned.
+ ///
+ /// \param val Number to convert to a logging level. This is an absolute
+ /// logging level number, not a debug level.
+ /// \param defaultLevel Logging level to return if value is not recognised.
+ ///
+ /// \return Pointer to the desired logging level object.
+ static LevelPtr toLevel(int val, const LevelPtr& defaultLevel);
+
+ /// \param Convert String to Logging Level
+ ///
+ /// Returns a logging level object corresponding to the given name. If the
+ /// name is invalid, an object of logging level DEBUG (the minimum debug
+ /// logging level) is returned.
+ ///
+ /// \param sArg Name of the logging level.
+ ///
+ /// \return Pointer to the desired logging level object.
+ static LevelPtr toLevelLS(const LogString& sArg);
+
+ /// \param Convert String to Logging Level
+ ///
+ /// Returns a logging level object corresponding to the given name. If the
+ /// name is invalid, the given default is returned.
+ ///
+ /// \param sArg name of the level.
+ /// \param defaultLevel Logging level to return if name doesn't exist.
+ ///
+ /// \return Pointer to the desired logging level object.
+ static LevelPtr toLevelLS(const LogString& sArg,
+ const LevelPtr& defaultLevel);
+};
+
+} // namespace log4cxx
+
+
+#endif // __XDEBUGLEVEL_H
More information about the bind10-changes
mailing list