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

Check-in of some code prior to conversion of repository to git; the code is not finished!

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac438@4169 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
Stephen Morris
2011-01-05 15:55:55 +00:00
parent 17684b0c3c
commit ebb344c1e2
28 changed files with 3450 additions and 2 deletions

View File

@@ -327,6 +327,39 @@ 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"])
# If not specified, try some common paths. These default to
# /usr/include 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
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)
#
# Configure Boost header path
#
@@ -612,6 +645,7 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/tests/Makefile
src/lib/xfr/Makefile
src/lib/log/Makefile
src/lib/log/tests/Makefile
src/lib/testutils/Makefile
src/lib/testutils/testdata/Makefile
src/lib/nsas/Makefile
@@ -721,6 +755,7 @@ dnl includes too
${PYTHON_LDFLAGS}
${PYTHON_LIB}
Boost: ${BOOST_INCLUDES}
log4cxx: ${LOG4CXX_INCLUDES}
SQLite: $SQLITE_CFLAGS
$SQLITE_LIBS

View File

@@ -1,4 +1,27 @@
AM_CXXFLAGS = $(B10_CXXFLAGS)
SUBDIRS = . tests
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
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = liblog.la
liblog_la_SOURCES = dummylog.cc dummylog.h
liblog_la_SOURCES = root_logger_name.cc root_logger_name.h
liblog_la_SOURCES += xdebuglevel.cc xdebuglevel.h
liblog_la_SOURCES += logger.cc logger.h
liblog_la_SOURCES += message_reader.cc message_reader.h
liblog_la_SOURCES += filename.h filename.cc
liblog_la_SOURCES += stringutil.h stringutil.cc
# 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)

72
src/lib/log/Message.h Normal file
View 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$
#ifndef __MESSAGE_H
#define __MESSAGE_H
/// \brief Log Message
///
/// This class represents a message in which information about a logging event
/// is encoded. This is invisible to the author of code doing logging unless
/// they are planning on writing an Appender.
class Message {
public:
// The following is a placeholder. It will be replaced with a finer-
// grained time definition during implementation.
typedef time_t Time; ///< To be replaced with a finer-grained time later
// Constructor/destructor stll to be determined
/// \brief Return Timestamp
///
/// \return Timestamp associated with the message.
Time getTimestamp() const;
/// \brief Return Source
///
/// \return Returns the source of the message. This is a "."-separated
/// string containing the hierarchy of the logger than generated this
/// message.
std::string getSource() const;
/// \brief Return ID
///
/// \return Returns the ID of the message, a 32-bit integer.
uint32_t getId() const;
/// \brief Return Parameters
///
/// \return The parameters of the message in the form of a vector of
/// strings. Numeric parameters have been converted to strings and
/// included in the message.
std::vector<std::string> getParameters() const;
/// \brief Return Encoded Message
///
/// The contents of the message are encoded as a string in the form
///
/// <message ID><'\0'><param 1><'\0'>...
///
/// Some Appenders may find this format useful, so the ability to retrieve
/// it directly is provided.
std::string getRawMessage() const;
};
#endif // __MESSAGE_H

119
src/lib/log/README.txt Normal file
View File

@@ -0,0 +1,119 @@
Logging Messages
Message Storage
===============
Each message is identified by a string identifier, e.g. "INVFILNAM", which is
associated with some text (e.g. "%s is an invalid file name"). These are stored
in a single std::map, in a class called the Dictionary.
The message identifier (along with parameters) is passed through the logging
system to an appender, which uses the identifier to look up the message in
the dictionary. The message is then formatted and written out.
Message File
============
A message file is a file containing message definitions. Typically there will
be one message file for each component that declares message symbols.
A example file could
be:
# 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.
Point to note:
* Leading and trailing space are trimmed from the line.
* Blank lines are ignored
* Lines starting with "#" are comments are are ignored.
* 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.
* Lines starting + indicate an explanation for the preceding message. These
are processed by a separate program and used to generate an error messages
manual. However they are treated like comments here.
* Message lines. These comprise a symbol name and a message (which includes
C-style substitution strings).
Message Compiler
================
The message compiler produces two files:
1) A C++ header file (called <message-file-name>.h) that holds lines of the
form:
namespace {
const char* PREFIX_IDENTIFIER = "identifier";
:
}
These are just convenience symbols to avoid the need to type in the string in
quotes. PREFIX_ is the string in the $PREFIX directive and is used to avoid
potential clashes with system-defined symbols.
2) A C++ source file (called <message-file-name>.cpp) 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* messages = {
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:
DictionaryAppender <message-file-name>_<prefix>_<time>(messages);
(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 DictionaryAppender object retrieves the singleton
global Dictionary object (created using standard methods to avoid the "static
initialization fiasco") and adds each identifier and text to the member
std::map. 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).
User Message Files
==================
During logging initialization, a search is made for a user message file in a
specific location. (The specification of the location has yet to be decided -
it will probably be a command-line option.) These messages are read into a
local Dictionary object (which performs the same checks as the global
Dictionary for duplicate messages).
The local Dictionary is then merged with the global Dictionary. In this case
though, warnings are issued where a message does not replace one in the global
Dictionary.
An additional check that could be done is to compare the user message string
with the main message string and to check that they have the same number of
"%s" components. This will avoid potential problems in message formatting. (As
noted in another design document, the intention within logging is to convert
all parameters to strings at the point at which the logging call is made.)
Message Compiler Implementation
===============================
The fact that user files are read in at run-time implies that the code that
reads the files should be C++. An implication of this is that the message
compiler should be written in C++ (instead of Python, which is probably
better for the task) to avoid two sets of code where message files are parsed.

41
src/lib/log/appender.h Normal file
View 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$
#ifndef __APPENDER_H
#define __APPENDER_H
#include <message.h>
/// \brief Abstract Appender Class
///
/// This class is responsible for writing messages generated by loggers
/// to destinations. It is an abstract class; particular appenders will
/// control different destinations.
///
/// Multiple appenders can be attached to a logger. Output is routed to
/// all appenders attached to a logger. The logger hierachy is then walked
/// and the message passed to the appenders in parent loggers.
class AbstractAppender {
public:
/// \brief Write the Output
///
/// Formats the message and writes it to the specified output.
void write(Message& message);
};
#endif // __APPENDER_H

View File

@@ -0,0 +1,130 @@
// 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 <unistd.h>
#include <getopt.h>
using namespace std;
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 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'},
{"python", no_argument, NULL, 'p'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0 }
};
const char* soptions = "hpv"; // Short options
optind = 1; // Ensure we start a new scan
int opt; // Value of the option
bool python = false; // Set true if the -p flag is detected
while ((opt = getopt_long(argc, argv, soptions, loptions, NULL)) != -1) {
switch (opt) {
case 'h':
usage();
return 0;
case 'v':
version();
return 0;
case 'p':
python = true;
break;
default:
// A message will have already been output about the error.
return 1;
}
}
// Do we have the message file?
if (optind < (argc - 1)) {
std::cout << "Error: excess arguments in command line\n";
usage();
return 1;
}
else if (optind >= argc) {
std::cout << "Error: missing message file\n";
usage();
return 1;
}
// Have identified the file, so process it.
MessageFileProcessor fileProcessor();
return fileProcessor.process(argv[argc - 1], python);
}

31
src/lib/log/dbglevels.h Normal file
View 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

132
src/lib/log/filename.cc Normal file
View File

@@ -0,0 +1,132 @@
// 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 <filename.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 = StringUtil::trim(defname);
StringUtil::normalizeSlash(copy_defname);
// 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 = StringUtil::trim(name);
StringUtil::normalizeSlash(copy_name);
// 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
View 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 <stringutil.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_ = StringUtil::trim(name);
#ifdef WIN32
StringUtil::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

50
src/lib/log/log.h Normal file
View File

@@ -0,0 +1,50 @@
// 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 __LOG_H
#define __LOG_H
namespace isc {
namespace log {
class RootLogger : public Logger
{
public:
/// \brief Return Root Logger
///
/// Returns the root logger for the system. Only one root logger is
/// defined and the name corresponds to the
static Log* getRootLogger() {
static Log root_logger();
return &root_logger;
}
/// \brief Set Logger Name
///
/// Sets the name of the root logger. This is dynamic, and
private:
static std::string root_name_;
};
}
}
#endif // __LOG_H

187
src/lib/log/logger.cc Normal file
View File

@@ -0,0 +1,187 @@
// 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 SOFTWAR
// $Id$
#include <iostream>
#include <log/root_logger_name.h>
#include <log/logger.h>
#include <log/xdebuglevel.h>
using namespace std;
namespace isc {
namespace log {
// Constructor - create a logger as a child of the root logger. With log4cxx
// this is assured by naming the logger <parent>.<child>.
Logger::Logger(const std::string& name) : loggerptr_()
{
string root_name = RootLoggerName::getName();
if (root_name.empty() || (name == root_name)) {
fullname_ = name;
}
else {
fullname_ = root_name + "." + name;
}
loggerptr_ = log4cxx::Logger::getLogger(fullname_);
}
// 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, WARNING 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 WARNING 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
// WARNING (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 = 99974. As 99974 !>= 99975, it is not logged. A
// message of level 25 is, because 99975 >= 99975.
//
// The extended set of logging levels is implemented by the XDebugLevel class.
void Logger::setSeverity(Severity severity, int dbglevel) {
switch (severity) {
case NONE:
loggerptr_->setLevel(
log4cxx::Level::toLevel(
log4cxx::Level::OFF_INT));
break;
case FATAL:
loggerptr_->setLevel(
log4cxx::Level::toLevel(
log4cxx::Level::FATAL_INT));
break;
case ERROR:
loggerptr_->setLevel(
log4cxx::Level::toLevel(
log4cxx::Level::ERROR_INT));
break;
case WARNING:
loggerptr_->setLevel(
log4cxx::Level::toLevel(
log4cxx::Level::WARN_INT));
break;
case INFO:
loggerptr_->setLevel(
log4cxx::Level::toLevel(
log4cxx::Level::INFO_INT));
break;
case DEBUG:
loggerptr_->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:
loggerptr_->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 (WARNING);
} 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() const {
log4cxx::LevelPtr level = loggerptr_->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);
}
}
}
} // namespace log
} // namespace isc

490
src/lib/log/logger.h Normal file
View File

@@ -0,0 +1,490 @@
// 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 <string>
#include <boost/lexical_cast.hpp>
#include <log4cxx/logger.h>
#include <log/dbglevels.h>
namespace isc {
namespace log {
class Logger {
public:
typedef int MessageCode; ///< Type of the message code
/// \brief Severity Levels
typedef enum {
DEFAULT, // Default to logging level of parent
DEBUG,
INFO,
WARNING,
ERROR,
CRITICAL,
NONE, // Disable logging
INFORMATION = INFO,
WARN = WARNING,
FATAL = CRITICAL
} Severity;
/// \brief Return a logger of a given name
///
/// Returns a logger with the specified name.
///
/// \param name Name of the logger. Unless specified as a root logger
/// (with a call to setRootLoggerName), the returned logger is a child
/// of the root logger.
///
/// \return Pointer to Logger object
// static Logger* getLogger(const char* name) {}
/// \brief Set Root Logger Name
///
/// One of the first calls in the program, this sets the name of the
/// root logger. (The name appears in logging messages.)
///
/// \param name Name of the root logger.
// static void setRootLoggerName(const char* name);
/// \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
/// chold of the root logger.
Logger(const std::string& name);
/// \brief Get Name of Logger
///
/// \return The full name of the logger (including the root name)
virtual std::string getName() const {
return loggerptr_->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() const {
return getSeverityCommon(loggerptr_, 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() const {
return getSeverityCommon(loggerptr_, true);
}
/// \brief Return DEBUG Level
///
/// \return Current setting of debug level. This is returned regardless of
/// whether the
virtual int getDebugLevel() const;
/// \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) const {
return (loggerptr_->getEffectiveLevel()->toInt() <=
(log4cxx::Level::DEBUG_INT - dbglevel));
}
/// \brief Is INFO Enabled?
virtual bool isInfoEnabled() const {
return (loggerptr_->isInfoEnabled());
}
/// \brief Is WARNING Enabled?
virtual bool isWarnEnabled() const {
return (loggerptr_->isWarnEnabled());
}
/// \brief Is WARNING Enabled?
virtual bool isWarningEnabled() const {
return (loggerptr_->isWarnEnabled());
}
/// \brief Is ERROR Enabled?
virtual bool isErrorEnabled() const {
return (loggerptr_->isErrorEnabled());
}
/// \brief Is CRITICAL Enabled?
virtual bool isCriticalEnabled() const {
return (loggerptr_->isFatalEnabled());
}
/// \brief Is FATAL Enabled?
///
/// FATAL is a synonym for CRITICAL.
virtual bool isFatalEnabled() const {
return (loggerptr_->isFatalEnabled());
}
/*
/// \brief Add Appender
///
/// Adds an appender to the list of appenders for this logger. The
/// appender is assumed to have an independent existence so although
/// a pointer to the appender is added here, the logger does not
/// assume responsibility for its destruction.
///
/// \param appender Pointer to the appender that should be added.
/// If the appender is already added to this logger, a duplicate
/// is not added.
///
/// \return true if the logger was added, false if it was already in the
/// list of appenders for this logger.
virtual bool addAppender(AbstractAppender* appender);
/// \brief Remove Appender
///
/// Removes the appender from the list of appenders for this logger.
///
/// \param appender Pointer to the appender that should be removed.
///
/// \return true if the appender was removed, false if it could not be
/// found in the list.
virtual bool removeAppender(AbstractAppender* appender);
/// \brief Get Effective Level for Logger
///
/// Gets the current effective logging level. If the current logger does
/// not have a level set, the inheritance tree is traversed until a level
/// is found.
virtual Level getEffectiveLevel() const;
/// \brief Debug Messages
///
/// A set of functions that control the output of the message and up to
/// four parameters.
void debugCommon(MessageCode code, std::string arg);
template <typename T1>
void debug(Level level, MessageCode code, T1 arg1) {
if (shouldOutputDebug(level)) {
debugCommon(code,
boost::lexical_cast<std::string>(arg1)
);
}
}
template <typename T1, typename T2>
void debug(MessageCode code, T1 arg1, T2 arg2) {
if (shouldOutputDebug(level)) {
debugCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2)
);
}
}
template <typename T1, typename T2, typename T3>
void debug(MessageCode code, T1 arg1, T2 arg2, T3 arg3) {
if (shouldOutputDebug(level)) {
debugCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3)
);
}
}
template <typename T1, typename T2, typename T3, typename T4>
void debug(MessageCode code, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
if (shouldOutputDebug(level)) {
debugCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3) + std::string('\0') +
boost::lexical_cast<std::string>(arg4)
);
}
}
/// \brief Informational Messages
///
/// A set of functions that control the output of the message and up to
/// four parameters.
void infoCommon(MessageCode code, std::string arg);
template <typename T1>
void info(MessageCode code, T1 arg1) {
if (isInfoEnabled()) {
infoCommon(code,
boost::lexical_cast<std::string>(arg1)
);
}
}
template <typename T1, typename T2>
void info(MessageCode code, T1 arg1, T2 arg2) {
if (isInfoEnabled()) {
infoCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2)
);
}
}
template <typename T1, typename T2, typename T3>
void info(MessageCode code, T1 arg1, T2 arg2, T3 arg3) {
if (isInfoEnabled()) {
infoCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3)
);
}
}
template <typename T1, typename T2, typename T3, typename T4>
void info(MessageCode code, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
if (isInfoEnabled()) {
infoCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3) + std::string('\0') +
boost::lexical_cast<std::string>(arg4)
);
}
}
/// \brief Warning Messages
///
/// A set of functions that control the output of the message and up to
/// four parameters.
void warnCommon(MessageCode code, std::string arg);
template <typename T1>
void warn(MessageCode code, T1 arg1) {
if (isWarnEnabled()) {
warnCommon(code,
boost::lexical_cast<std::string>(arg1)
);
}
}
template <typename T1, typename T2>
void warn(MessageCode code, T1 arg1, T2 arg2) {
if (isWarnEnabled()) {
warnCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2)
);
}
}
template <typename T1, typename T2, typename T3>
void warn(MessageCode code, T1 arg1, T2 arg2, T3 arg3) {
if (isWarnEnabled()) {
warnCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3)
);
}
}
template <typename T1, typename T2, typename T3, typename T4>
void warn(MessageCode code, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
if (isWarnEnabled()) {
warnCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3) + std::string('\0') +
boost::lexical_cast<std::string>(arg4)
);
}
}
/// \brief Error Messages
///
/// A set of functions that control the output of the message and up to
/// four parameters.
void errorCommon(MessageCode code, std::string arg);
template <typename T1>
void error(MessageCode code, T1 arg1) {
if (isErrorEnabled()) {
errorCommon(code,
boost::lexical_cast<std::string>(arg1)
);
}
}
template <typename T1, typename T2>
void error(MessageCode code, T1 arg1, T2 arg2) {
if (isErrorEnabled()) {
errorCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2)
);
}
}
template <typename T1, typename T2, typename T3>
void error(MessageCode code, T1 arg1, T2 arg2, T3 arg3) {
if (isErrorEnabled()) {
errorCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3)
);
}
}
template <typename T1, typename T2, typename T3, typename T4>
void error(MessageCode code, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
if (isErrorEnabled()) {
errorCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3) + std::string('\0') +
boost::lexical_cast<std::string>(arg4)
);
}
}
/// \brief Critical Messages
///
/// A set of functions that control the output of the message and up to
/// four parameters.
void criticalCommon(MessageCode code, std::string arg);
template <typename T1>
void critical(MessageCode code, T1 arg1) {
if (isCriticalEnabled()) {
criticalCommon(code,
boost::lexical_cast<std::string>(arg1)
);
}
}
template <typename T1, typename T2>
void critical(MessageCode code, T1 arg1, T2 arg2) {
if (isCriticalEnabled()) {
criticalCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2)
);
}
}
template <typename T1, typename T2, typename T3>
void critical(MessageCode code, T1 arg1, T2 arg2, T3 arg3) {
if (isCriticalEnabled()) {
criticalCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3)
);
}
}
template <typename T1, typename T2, typename T3, typename T4>
void critical(MessageCode code, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
if (isCriticalEnabled()) {
errorCommon(code,
boost::lexical_cast<std::string>(arg1) + std::string('\0') +
boost::lexical_cast<std::string>(arg2) + std::string('\0') +
boost::lexical_cast<std::string>(arg3) + std::string('\0') +
boost::lexical_cast<std::string>(arg4)
);
}
}
*/
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_ != log4cxx::LoggerPtr());
}
/// \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;
private:
log4cxx::LoggerPtr loggerptr_; ///< Pointer to the underlying logger
std::string fullname_; ///< Full name of this logger
};
} // namespace log
} // namespace isc
#endif // __LOGGER_H

View File

@@ -0,0 +1,137 @@
// 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_reader.h>
#include <log/stringutil.h>
using namespace std;
namespace isc {
namespace log {
// Virtual destructor.
MessageReader::~MessageReader() {
}
// Return error text
string MessageReader::errorText(MessageReader::Status status) {
switch (status) {
case SUCCESS:
return "Success";
case DUPLPRFX:
return "Error, duplicate $PREFIX directive found";
case PRFXEXTRARG:
return "Error, $PREFIX directive has extra arguments";
case PRFXINVARG:
return "Error, $PREFIX directive has an invalid argument";
case PRFXNOARG:
return "Error, $PREFIX directive has no arguments";
case UNRECDIR:
return "Error, unrecognised directive";
default:
return "Unknown message code";
}
}
// Read the file
MessageReader::Status MessageReader::readFile(const string&) {
return OPENIN;
}
// Clear the Message Map
void MessageReader::clear() {
}
// Parse a line of the file
MessageReader::Status MessageReader::processLine(const string& line) {
// Get rid of leading and trailing spaces
string text = StringUtil::trim(line);
if (text.empty()) {
return SUCCESS; // Ignore blank lines
} else if ((text[0] == '#') || (text[0] == '+')) {
return SUCCESS; // Ignore comments or descriptions
} else if (text[0] == '$') {
return directive(text); // Process directives
} else {
return OPENIN;
}
}
// Process directive
MessageReader::Status MessageReader::directive(const std::string& text) {
static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
// Regardless of what happens, all prefixes will be uppercase (as will
// all symbols).
string line = text;
StringUtil::uppercase(line);
vector<string> tokens = StringUtil::tokens(line);
// Only $PREFIX is recognised so far, so we'll handle it here.
if (tokens[0] != string("$PREFIX")) {
return UNRECDIR; // Directive is not prefix
} else if (tokens.size() < 2) {
return PRFXNOARG; // Does not have an argument
} else if (tokens.size() > 2) {
return PRFXEXTRARG; // Too many arguments
}
// 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 opr it starts with a digit.
return PRFXINVARG;
}
// All OK - unless the prefix has already been set.
if (prefix_.size() != 0) {
return DUPLPRFX;
}
// Prefix has not been set, so set it and return success.
prefix_ = tokens[1];
return SUCCESS;
}
} // namespace log
} // namespace isc

View File

@@ -0,0 +1,156 @@
// 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>
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 Status Returns
///
/// It may seem odd that a class devoted to reading logfile messages does
/// not have its own set of messages. The reason is that this class is
/// used in both the server and in the message compiler. The latter is
/// a stand-along program used to create message files, so at the time this
/// file is compiled, there is nothing to build an associated message file.
typedef enum {
SUCCESS, // Success, all OK.
DUPLPRFX, // Error, duplicate prefix found
OPENIN, // Error openinin input file
PRFXEXTRARG, // Error, $PREFIX directive has extra arguments
PRFXINVARG, // Error, $PREFIX has invalid argument
PRFXNOARG, // Error, $PREFIX directive has no arguments
UNRECDIR // Error, unrecognised directive
} Status; // Status code
/// \brief Other Types
typedef std::map<std::string, std::string> MessageMap;
typedef std::vector<std::string> MessageDuplicates;
/// \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).
MessageReader() : messages_(), duplicates_()
{}
/// \brief Virtual Destructor
virtual ~MessageReader();
/// \brief Return Error Text
///
/// Returns the message associated with the error code
///
/// \param status Status code for which a message is required
///
/// \return Text of the error.
virtual std::string errorText(Status status);
/// \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 map.
///
/// \param file Name of the message file.
///
/// \return Status return. Should be SUCCESS, else an error has occurred.
virtual Status readFile(const std::string& file);
/// \brief Clears Message Mapgit find new files
///
/// In the event of an instance of the class needing to be reused, this
/// method will clear the message map and the list of duplicated.
virtual void clear();
/// \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
///
/// \return Status return
virtual Status processLine(const std::string& line);
/// \brief Return Message Map
///
/// Returns the message map.
///
/// \return Returns a copy of the internal map.
/// TODO: Usse a reference?
virtual MessageMap getMessageMap() const {
return messages_;
}
/// \brief Return Message Duplicates
///
/// Returns a copy of the duplicates vector.
///
/// \return Copy of the duplicates vector
virtual MessageDuplicates getMessageDuplicates() {
return duplicates_;
}
/// \brief Get Prefix
///
/// \return Argument to the $PREFIX directive (if present)
virtual std::string getPrefix() const {
return prefix_;
}
private:
/// \brief Handle Directive
///
/// Passed a line starting with a "$", this handles the processing of
/// directives.
///
/// \param line Line of text that starts with "$",
///
/// \return Status return code. NORMAL implies success
Status directive(const std::string& line);
/// Attributes
MessageMap messages_; // Message map
MessageDuplicates duplicates_; // Duplicate messages
std::string prefix_; // Input of $PREFIX statement
};
} // namespace log
} // namespace isc
#endif // __MESSAGE_READER_H

View 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_("");
}
}

View File

@@ -0,0 +1,57 @@
// 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 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

83
src/lib/log/stringutil.cc Normal file
View File

@@ -0,0 +1,83 @@
// 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.h>
#include <stringutil.h>
using namespace std;
namespace isc {
namespace log {
// Trim String
string StringUtil::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> StringUtil::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;
}
} // namespace log
} // namespace isc

129
src/lib/log/stringutil.h Normal file
View File

@@ -0,0 +1,129 @@
// 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 __STRINGUTIL_H
#define __STRINGUTIL_H
#include <algorithm>
#include <cctype>
#include <string>
#include <vector>
namespace isc {
namespace log {
/// \brief A Set of C++ Utilities for Manipulating Strings
class StringUtil {
public:
/// \brief Normalize Backslash
///
/// Only active on 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
static void normalizeSlash(std::string& name) {
if (! name.empty()) {
size_t pos = 0;
while ((pos = name.find('\\', pos)) != std::string::npos) {
name[pos] = '/';
}
}
}
/// \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
static 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.
///
/// 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.
static std::vector<std::string> tokens(const std::string text,
const std::string& delim = std::string(" \t\n"));
/// \brief Uppercase Character
///
/// Needed to pass as an argument to transform() in uppercase(), as the
/// function std::toupper() takes an "int" as its argument and the template
/// expansion mechanism gets confused.
///
/// \param chr Character to be upper-cased.
///
/// \return Uppercase version of the argument
static 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.
static void uppercase(std::string& text) {
std::transform(text.begin(), text.end(), text.begin(),
StringUtil::toUpper);
}
/// \brief Lowercase Character
///
/// Needed to pass as an argument to transform() in lowercase(), as the
/// function std::tolower() takes an "int" as its argument and the template
/// expansion mechanism gets confused.
///
/// \param chr Character to be lower-cased.
///
/// \return Lowercase version of the argument
static 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.
static void lowercase(std::string& text) {
std::transform(text.begin(), text.end(), text.begin(),
StringUtil::toLower);
}
};
} // namespace log
} // namespace isc
#endif // __STRINGUTIL_H

View File

@@ -0,0 +1,31 @@
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_reader_unittest.cc
run_unittests_SOURCES += stringutil_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
noinst_PROGRAMS = $(TESTS)

View File

@@ -0,0 +1,146 @@
// 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());
// Empty string
fname.setName("");
EXPECT_EQ("", fname.directory());
EXPECT_EQ("", fname.name());
EXPECT_EQ("", fname.extension());
// ... and finally 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());
}
// 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(""));
}

View File

@@ -0,0 +1,383 @@
// 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>
using namespace isc;
using namespace isc::log;
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 std::string& name) : Logger(name)
{}
/// \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(std::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 std::string name1 = "alpha";
const std::string name2 = "beta";
// Instantiate two loggers that should be the same
TestLogger logger1(name1);
TestLogger logger2(name1);
// And check they are equal and non-null
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_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::WARNING, 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::CRITICAL, 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");
Logger logger("alpha");
// Now check the levels
logger.setSeverity(Logger::NONE);
EXPECT_EQ(Logger::NONE, logger.getSeverity());
logger.setSeverity(Logger::CRITICAL);
EXPECT_EQ(Logger::CRITICAL, logger.getSeverity());
logger.setSeverity(Logger::ERROR);
EXPECT_EQ(Logger::ERROR, logger.getSeverity());
logger.setSeverity(Logger::WARNING);
EXPECT_EQ(Logger::WARNING, 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");
Logger 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");
Logger parent("alpha");
Logger 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));
}

View 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: base64_unittest.cc 2549 2010-07-20 19:09:37Z jinmei $
#include <string>
#include <gtest/gtest.h>
#include <log/message_reader.h>
using namespace isc;
using namespace isc::log;
using namespace std;
class MessageReaderTest : public ::testing::Test {
protected:
MessageReaderTest() : reader_()
{
}
MessageReader reader_; // Default reader object
};
// Check for a couple of status messages
TEST_F(MessageReaderTest, StatusMessage) {
string unknown("Unknown message code"); // Default unknown message
EXPECT_NE(unknown, reader_.errorText(MessageReader::SUCCESS));
EXPECT_NE(unknown, reader_.errorText(MessageReader::DUPLPRFX));
EXPECT_NE(unknown, reader_.errorText(MessageReader::PRFXEXTRARG));
EXPECT_NE(unknown, reader_.errorText(MessageReader::PRFXINVARG));
EXPECT_NE(unknown, reader_.errorText(MessageReader::PRFXNOARG));
EXPECT_NE(unknown, reader_.errorText(MessageReader::UNRECDIR));
}
// Check for parsing blank lines and comments. These should not add to the map and
// each parse should return success.
TEST_F(MessageReaderTest, BlanksAndComments) {
// Ensure that the map is empty
MessageReader::MessageMap mmap = reader_.getMessageMap();
EXPECT_EQ(0, mmap.size());
MessageReader::MessageDuplicates mduplicates = reader_.getMessageDuplicates();
EXPECT_EQ(0, mduplicates.size());
// Add a number of blank lines and comments and check that (a) they are parsed
// successfully ...
MessageReader::Status status = reader_.processLine("");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine(" ");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine(" \n ");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine("# This is a comment");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine("\t\t # Another comment");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine(" + A description line");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine("#+ A comment");
EXPECT_EQ(MessageReader::SUCCESS, status);
status = reader_.processLine(" +# A description line");
EXPECT_EQ(MessageReader::SUCCESS, status);
// ... and (b) nothing gets added to either the map or the duplicates
mmap = reader_.getMessageMap();
EXPECT_EQ(0, mmap.size());
mduplicates = reader_.getMessageDuplicates();
EXPECT_EQ(0, mduplicates.size());
}
// 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.
EXPECT_EQ(MessageReader::PRFXNOARG, reader_.processLine("$PREFIX"));
// Check a prefix with multiple arguments is invalid
EXPECT_EQ(MessageReader::PRFXEXTRARG, reader_.processLine("$prefix A B"));
// Prefixes should be alphanumeric (with underscores) and not start
// with a number.
EXPECT_EQ(MessageReader::PRFXINVARG, reader_.processLine("$prefix ab[cd"));
EXPECT_EQ(MessageReader::PRFXINVARG, reader_.processLine("$prefix 123"));
EXPECT_EQ(MessageReader::PRFXINVARG, reader_.processLine("$prefix 1ABC"));
// A valid prefix should be accepted
EXPECT_EQ(MessageReader::SUCCESS, reader_.processLine("$PREFIX dlm__"));
EXPECT_EQ(string("DLM__"), reader_.getPrefix());
// And check that the parser fails on invalid prefixes...
EXPECT_EQ(MessageReader::PRFXINVARG, reader_.processLine("$prefix 1ABC"));
// ... and rejects another valid one
EXPECT_EQ(MessageReader::DUPLPRFX, reader_.processLine("$PREFIX ABC"));
}

View 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());
}

View 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());
}

View File

@@ -0,0 +1,122 @@
// 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/stringutil.h>
using namespace isc;
using namespace isc::log;
using namespace std;
class StringUtilTest : public ::testing::Test {
protected:
StringUtilTest()
{
}
};
// Check for slash replacement
TEST_F(StringUtilTest, Slash) {
string instring = "";
StringUtil::normalizeSlash(instring);
EXPECT_EQ("", instring);
instring = "C:\\A\\B\\C.D";
StringUtil::normalizeSlash(instring);
EXPECT_EQ("C:/A/B/C.D", instring);
instring = "// \\ //";
StringUtil::normalizeSlash(instring);
EXPECT_EQ("// / //", instring);
}
// Check that leading and trailing space trimming works
TEST_F(StringUtilTest, Trim) {
// Empty and full string.
EXPECT_EQ("", StringUtil::trim(""));
EXPECT_EQ("abcxyz", StringUtil::trim("abcxyz"));
// Trim right-most blanks
EXPECT_EQ("ABC", StringUtil::trim("ABC "));
EXPECT_EQ("ABC", StringUtil::trim("ABC\t\t \n\t"));
// Left-most blank trimming
EXPECT_EQ("XYZ", StringUtil::trim(" XYZ"));
EXPECT_EQ("XYZ", StringUtil::trim("\t\t \tXYZ"));
// Right and left, with embedded spaces
EXPECT_EQ("MN \t OP", StringUtil::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
result = StringUtil::tokens(" \n "); // Empty string
EXPECT_EQ(0, result.size());
result = StringUtil::tokens("abc"); // Full string
ASSERT_EQ(1, result.size());
EXPECT_EQ(string("abc"), result[0]);
result = StringUtil::tokens("\t xyz \n");
ASSERT_EQ(1, result.size());
EXPECT_EQ(string("xyz"), result[0]);
result = StringUtil::tokens("abc\ndef\t\tghi ");
ASSERT_EQ(3, result.size());
EXPECT_EQ(string("abc"), result[0]);
EXPECT_EQ(string("def"), result[1]);
EXPECT_EQ(string("ghi"), result[2]);
// Non-default delimiters
result = StringUtil::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;
StringUtil::lowercase(test);
EXPECT_EQ(lower, test);
test = mixed;
StringUtil::uppercase(test);
EXPECT_EQ(upper, test);
}

View 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);
}

145
src/lib/log/xdebuglevel.cc Normal file
View File

@@ -0,0 +1,145 @@
// 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
View 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 does not exist.
///
/// \return Pointer to the desired logging level object.
static LevelPtr toLevelLS(const LogString& sArg,
const LevelPtr& defaultLevel);
};
} // namespace log4cxx
#endif // __XDEBUGLEVEL_H