mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
[master] Merge branch 'trac438'
Conflicts: configure.ac
This commit is contained in:
50
configure.ac
50
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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
20
src/lib/log/compiler/Makefile.am
Normal file
20
src/lib/log/compiler/Makefile.am
Normal file
@@ -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
|
||||
|
450
src/lib/log/compiler/message.cc
Normal file
450
src/lib/log/compiler/message.cc
Normal file
@@ -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;
|
||||
|
||||
}
|
31
src/lib/log/dbglevels.h
Normal file
31
src/lib/log/dbglevels.h
Normal file
@@ -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
|
371
src/lib/log/documentation.txt
Normal file
371
src/lib/log/documentation.txt
Normal file
@@ -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.
|
||||
|
140
src/lib/log/filename.cc
Normal file
140
src/lib/log/filename.cc
Normal file
@@ -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
|
163
src/lib/log/filename.h
Normal file
163
src/lib/log/filename.h
Normal file
@@ -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
|
307
src/lib/log/logger.cc
Normal file
307
src/lib/log/logger.cc
Normal file
@@ -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
|
327
src/lib/log/logger.h
Normal file
327
src/lib/log/logger.h
Normal file
@@ -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
|
116
src/lib/log/logger_support.cc
Normal file
116
src/lib/log/logger_support.cc
Normal file
@@ -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
|
43
src/lib/log/logger_support.h
Normal file
43
src/lib/log/logger_support.h
Normal file
@@ -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
|
116
src/lib/log/message_dictionary.cc
Normal file
116
src/lib/log/message_dictionary.cc
Normal file
@@ -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
|
150
src/lib/log/message_dictionary.h
Normal file
150
src/lib/log/message_dictionary.h
Normal file
@@ -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
|
28
src/lib/log/message_exception.cc
Normal file
28
src/lib/log/message_exception.cc
Normal file
@@ -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
|
90
src/lib/log/message_exception.h
Normal file
90
src/lib/log/message_exception.h
Normal file
@@ -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
|
32
src/lib/log/message_initializer.cc
Normal file
32
src/lib/log/message_initializer.cc
Normal file
@@ -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
|
63
src/lib/log/message_initializer.h
Normal file
63
src/lib/log/message_initializer.h
Normal file
@@ -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
|
184
src/lib/log/message_reader.cc
Normal file
184
src/lib/log/message_reader.cc
Normal file
@@ -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
|
175
src/lib/log/message_reader.h
Normal file
175
src/lib/log/message_reader.h
Normal file
@@ -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
|
32
src/lib/log/message_types.h
Normal file
32
src/lib/log/message_types.h
Normal file
@@ -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
|
27
src/lib/log/messagedef.cc
Normal file
27
src/lib/log/messagedef.cc
Normal file
@@ -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);
|
24
src/lib/log/messagedef.h
Normal file
24
src/lib/log/messagedef.h
Normal file
@@ -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
|
82
src/lib/log/messagedef.mes
Normal file
82
src/lib/log/messagedef.mes
Normal file
@@ -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.
|
26
src/lib/log/root_logger_name.cc
Normal file
26
src/lib/log/root_logger_name.cc
Normal file
@@ -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_("");
|
||||
|
||||
}
|
||||
}
|
66
src/lib/log/root_logger_name.h
Normal file
66
src/lib/log/root_logger_name.h
Normal file
@@ -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
|
138
src/lib/log/strutil.cc
Normal file
138
src/lib/log/strutil.cc
Normal file
@@ -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
|
147
src/lib/log/strutil.h
Normal file
147
src/lib/log/strutil.h
Normal file
@@ -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
|
45
src/lib/log/tests/Makefile.am
Normal file
45
src/lib/log/tests/Makefile.am
Normal file
@@ -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
|
181
src/lib/log/tests/filename_unittest.cc
Normal file
181
src/lib/log/tests/filename_unittest.cc
Normal file
@@ -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(""));
|
||||
}
|
23
src/lib/log/tests/localdef.mes
Normal file
23
src/lib/log/tests/localdef.mes
Normal file
@@ -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'
|
109
src/lib/log/tests/logger_support_test.cc
Normal file
109
src/lib/log/tests/logger_support_test.cc
Normal file
@@ -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;
|
||||
}
|
395
src/lib/log/tests/logger_unittest.cc
Normal file
395
src/lib/log/tests/logger_unittest.cc
Normal file
@@ -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));
|
||||
}
|
173
src/lib/log/tests/message_dictionary_unittest.cc
Normal file
173
src/lib/log/tests/message_dictionary_unittest.cc
Normal file
@@ -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"));
|
||||
}
|
72
src/lib/log/tests/message_initializer_unittest.cc
Normal file
72
src/lib/log/tests/message_initializer_unittest.cc
Normal file
@@ -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"));
|
||||
}
|
41
src/lib/log/tests/message_initializer_unittest_2.cc
Normal file
41
src/lib/log/tests/message_initializer_unittest_2.cc
Normal file
@@ -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);
|
228
src/lib/log/tests/message_reader_unittest.cc
Normal file
228
src/lib/log/tests/message_reader_unittest.cc
Normal file
@@ -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]);
|
||||
}
|
52
src/lib/log/tests/root_logger_name_unittest.cc
Normal file
52
src/lib/log/tests/root_logger_name_unittest.cc
Normal file
@@ -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());
|
||||
}
|
84
src/lib/log/tests/run_time_init_test.sh.in
Executable file
84
src/lib/log/tests/run_time_init_test.sh.in
Executable file
@@ -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
|
23
src/lib/log/tests/run_unittests.cc
Normal file
23
src/lib/log/tests/run_unittests.cc
Normal file
@@ -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());
|
||||
}
|
216
src/lib/log/tests/strutil_unittest.cc
Normal file
216
src/lib/log/tests/strutil_unittest.cc
Normal file
@@ -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));
|
||||
}
|
205
src/lib/log/tests/xdebuglevel_unittest.cc
Normal file
205
src/lib/log/tests/xdebuglevel_unittest.cc
Normal file
@@ -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);
|
||||
}
|
148
src/lib/log/xdebuglevel.cc
Normal file
148
src/lib/log/xdebuglevel.cc
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
164
src/lib/log/xdebuglevel.h
Normal file
164
src/lib/log/xdebuglevel.h
Normal file
@@ -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
|
Reference in New Issue
Block a user