Commit ebb344c1 authored by Stephen Morris's avatar Stephen Morris

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
parent 17684b0c
......@@ -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
......
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)
// 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
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.
// 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
// 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);
}
// 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
// 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
// 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