mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
[master] Merge branch 'trac2566'
This commit is contained in:
@@ -86,10 +86,10 @@
|
||||
* - @subpage libdhcp_ddns
|
||||
*
|
||||
* @section miscellaneousTopics Miscellaneous Topics
|
||||
* - @subpage LoggingApi
|
||||
* - @subpage LoggingApiOverview
|
||||
* - @subpage LoggingApiLoggerNames
|
||||
* - @subpage LoggingApiLoggingMessages
|
||||
* - @subpage logBind10Logging
|
||||
* - @subpage logBasicIdeas
|
||||
* - @subpage logDeveloperUse
|
||||
* - @subpage logNotes
|
||||
* - @subpage SocketSessionUtility
|
||||
* - <a href="./doxygen-error.log">Documentation warnings and errors</a>
|
||||
*
|
||||
|
@@ -32,7 +32,7 @@ libb10_log_la_SOURCES += message_types.h
|
||||
libb10_log_la_SOURCES += output_option.cc output_option.h
|
||||
libb10_log_la_SOURCES += buffer_appender_impl.cc buffer_appender_impl.h
|
||||
|
||||
EXTRA_DIST = README
|
||||
EXTRA_DIST = logging.dox
|
||||
EXTRA_DIST += logimpl_messages.mes
|
||||
EXTRA_DIST += log_messages.mes
|
||||
|
||||
|
@@ -1,529 +0,0 @@
|
||||
This directory holds the first release of the logging system.
|
||||
|
||||
|
||||
Basic Ideas
|
||||
===========
|
||||
The BIND-10 logging system merges two ideas:
|
||||
|
||||
* A hierarchical logging system similar to that used in Java (i.e. log4j)
|
||||
* Separation of message use from message text
|
||||
|
||||
|
||||
Hierarchical Logging System
|
||||
===========================
|
||||
When a program writes a message to the logging system, it does so using an
|
||||
instance of the Logger class. As well as performing the write of the message,
|
||||
the logger identifies the source of the message: different sources can write
|
||||
to different destinations and can log different severities of messages.
|
||||
For example, the "cache" logger could write messages of DEBUG severity or
|
||||
above to a file while all other components write messages of "INFO" severity
|
||||
or above to the Syslog file.
|
||||
|
||||
The loggers are hierarchical in that each logger is the child of another
|
||||
logger. The top of the hierarchy is the root logger, which does not have
|
||||
a parent. The point of the hierarchy is that unless a logger is explicitly
|
||||
assigned an attribute (such as severity of message being logger), it picks
|
||||
it up from the parent. (In BIND-10, there is the root logger (named after
|
||||
the program) and every other logger is a child of that.) So in the example
|
||||
above, the INFO/Syslog attributes could be associated with the root logger
|
||||
while the DEBUG/file attributes are associated with the "cache" logger.
|
||||
|
||||
|
||||
Separation of Messages Use from Message Text
|
||||
============================================
|
||||
By separating the use of the message from the text associated with this -
|
||||
in essence, defining message text in an external file - it is possible to
|
||||
replace the supplied text of the messages with a local language version.
|
||||
|
||||
Each message is identified by an identifier e.g. "LOG_WRITE_ERROR".
|
||||
Within the program, this is the symbol passed to the logging system.
|
||||
The logger system uses the symbol as an index into a dictionary to
|
||||
retrieve the message associated with it (e.g. "unable to open %s for
|
||||
input"). It then substitutes any message parameters (in this example,
|
||||
the name of the file where the write operation failed) and logs it to
|
||||
the destination.
|
||||
|
||||
In BIND-10, a the default text for each message is linked into the
|
||||
program. Each program is able to read a locally-defined message file
|
||||
when it starts, updating the stored definitions with site-specific text.
|
||||
When the message is logged, the updated text is output. However, the
|
||||
message identifier is always included in the output so that the origin
|
||||
of the message can be identified even if the text has been changed.
|
||||
|
||||
|
||||
Using The System
|
||||
================
|
||||
The steps in using the system are:
|
||||
|
||||
1. Create a message file. This defines messages by an identification - a
|
||||
mnemonic for the message, the convention being that these are a few
|
||||
words separated by underscores - and text that explains the message in
|
||||
more detail. The file is described in more detail below.
|
||||
|
||||
Ideally the file should have a file type of ".mes".
|
||||
|
||||
2. Run it through the message compiler to produce the .h and .cc files. This
|
||||
step should be included in the build process. (For an example of how to
|
||||
do this, see src/lib/nsas/Makefile.am.) During development though, the
|
||||
message compiler (found in the "src/lib/log/compiler" directory) will need
|
||||
to be run manually. The only argument is the name of the message file: it
|
||||
will produce as output two files in the default directory, having the same
|
||||
name as the input file but with file types of ".h" and ".cc".
|
||||
|
||||
3. Include the .h file in your source code to define message symbols, and
|
||||
make sure that the .cc file is compiled and linked into your program -
|
||||
static initialization will add the symbols to the global dictionary.
|
||||
|
||||
4. Declare loggers in your code and use them to log messages. This is
|
||||
described in more detail below.
|
||||
|
||||
5. To set the debug level and run-time message file, call initLogger (declared
|
||||
in logger_support.h) in the main program unit.
|
||||
|
||||
|
||||
Message Files
|
||||
=============
|
||||
|
||||
File Contents and Format
|
||||
------------------------
|
||||
A message file is a file containing message definitions. Typically there
|
||||
will be one message file for each component that declares message symbols.
|
||||
An example file could be:
|
||||
|
||||
-- BEGIN --
|
||||
|
||||
# Example message file
|
||||
|
||||
$NAMESPACE isc::log
|
||||
|
||||
% LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'
|
||||
A line starting with a dollar symbol was found, but the first word on the line
|
||||
(shown in the message) was not a recognised message compiler directive.
|
||||
|
||||
% LOG_WRITE_ERROR error writing to %1: %2
|
||||
The specified error was encountered by the message compiler when writing to
|
||||
the named output file.
|
||||
|
||||
-- END --
|
||||
|
||||
Points to note:
|
||||
* Leading and trailing space are trimmed from the line. Although the above
|
||||
example has every line starting at column 1, the lines could be indented
|
||||
if desired.
|
||||
|
||||
* Blank lines are ignored.
|
||||
|
||||
* Lines starting with "#" are comments are are ignored. Comments must be on
|
||||
a line by themselves - inline comments will be interpreted as part of the
|
||||
text of the line.
|
||||
|
||||
* Lines starting $ are directives. At present, just one directive is
|
||||
recognised:
|
||||
|
||||
* $NAMESPACE, which has one argument: the namespace in which the symbols are
|
||||
created. In the absence of a $NAMESPACE directive, symbols will be put in
|
||||
the anonymous namespace.
|
||||
|
||||
* Message lines. These start with a "%" and are followed by the message
|
||||
identification and the message text, the latter including zero or more
|
||||
replacement tokens, e.g.
|
||||
|
||||
% LOG_WRITE_ERROR error writing to %1: %2
|
||||
|
||||
* There may be zero or more spaces between the leading "%" and the message
|
||||
identification (which, in the example above, is the string
|
||||
"LOG_WRITE_ERROR").
|
||||
|
||||
* The message identification can be any string of letters, digits and
|
||||
underscores, but should not start with a digit. The convention adopted
|
||||
in BIND 10 is for the first component (before the first underscore) to be
|
||||
a string indicating the origin of the message, and the remainder to
|
||||
describe the message. So in the example above, the LOG_ indicates that
|
||||
the error originated from the logging library and the "WRITE_ERROR"
|
||||
indicates that there was a problem in a write operation.
|
||||
|
||||
* The rest of the line - from the first non-space character to the
|
||||
last non- space character - is taken exactly for the text
|
||||
of the message. There are no restrictions on what characters may
|
||||
be in this text, other than they be printable. (This means that
|
||||
both single-quote (') and double-quote (") characters are allowed.)
|
||||
The message text may include replacement tokens (the strings "%1",
|
||||
"%2" etc.). When a message is logged, these are replaced with the
|
||||
arguments passed to the logging call: %1 refers to the first argument,
|
||||
%2 to the second etc. Within the message text, the placeholders
|
||||
can appear in any order and placeholders can be repeated. Otherwise,
|
||||
the message is printed unmodified.
|
||||
|
||||
* Remaining lines indicate an explanation for the preceding message. These
|
||||
are intended to be processed by a separate program and used to generate
|
||||
an error messages manual. They are ignored by the message compiler.
|
||||
|
||||
Message Compiler
|
||||
----------------
|
||||
The message compiler is a program built in the src/log/compiler directory.
|
||||
It is invoked by the command:
|
||||
|
||||
message [-h] [-v] -p] <message-file>
|
||||
|
||||
("-v" prints the version number and exits; "-h" prints brief help text.) The
|
||||
compiler produces source files for C++ and Python.
|
||||
|
||||
C++ Files
|
||||
---------
|
||||
Without the "-p" option, the message compiler processes the message file
|
||||
to produce two files:
|
||||
|
||||
1) A C++ header file (called <message-file-name>.h) that holds lines of
|
||||
the form:
|
||||
|
||||
namespace <namespace> {
|
||||
extern const isc::log::MessageID LOG_WRITE_ERROR;
|
||||
:
|
||||
}
|
||||
|
||||
The symbols define the keys in the global message dictionary, with the
|
||||
namespace enclosing the symbols set by the $NAMESPACE directive.
|
||||
|
||||
(This is the reason for the restriction on message identifiers - they
|
||||
have to be valid C++ symbol names.)
|
||||
|
||||
2) A C++ source file (called <message-file-name>.cc) that holds the definitions
|
||||
of the global symbols and code to insert the symbols and messages into the map.
|
||||
|
||||
Symbols are defined to be equal to strings holding the identifier, e.g.
|
||||
|
||||
extern const isc::log::MessageID LOG_WRITE_ERROR = "LOG_WRITE_ERROR";
|
||||
|
||||
(The implementation allows symbols to be compared. However, use of strings
|
||||
should not be assumed - a future implementation may change this.)
|
||||
|
||||
In addition, the file declares an array of identifiers/messages and an object
|
||||
to add them to the global dictionary:
|
||||
|
||||
namespace {
|
||||
const char* values[] = {
|
||||
identifier1, text1,
|
||||
identifier2, text2,
|
||||
:
|
||||
NULL
|
||||
};
|
||||
|
||||
const isc::log::MessageInitializer initializer(values);
|
||||
}
|
||||
|
||||
The constructor of the MessageInitializer object retrieves the singleton
|
||||
global Dictionary object (created using standard methods to avoid the
|
||||
"static initialization fiasco") and adds each identifier and text to it.
|
||||
A check is made as each is added; if the identifier already exists, it is
|
||||
added to "overflow" vector; the vector is printed to the main logging output
|
||||
when logging is finally enabled (to indicate a programming error).
|
||||
|
||||
Python Files
|
||||
------------
|
||||
If the "-p" option is given, the compiler produces a Python module defining
|
||||
the messages. The format of this is:
|
||||
|
||||
import isc.log
|
||||
:
|
||||
LOG_WRITE_ERROR = isc.log.create_message("LOG_WRITE_ERROR",
|
||||
"error writing to %1 : %2")
|
||||
|
||||
(The definition is output on one line - it is split across two lines in this
|
||||
document for readability.)
|
||||
|
||||
The module can be imported into other Python code, and messages logged
|
||||
in a similar way to C++ using the Python logging library.
|
||||
|
||||
Using the Logging - C++
|
||||
=======================
|
||||
1. Build message header file and source file as describe above.
|
||||
|
||||
2. The main program unit must include a call to isc::log::initLogger()
|
||||
(described in more detail below) to set the logging severity, debug log
|
||||
level, and external message file:
|
||||
|
||||
a) The logging severity is one of the enum defined in logger.h, i.e.
|
||||
|
||||
isc::log::DEBUG
|
||||
isc::log::INFO
|
||||
isc::log::WARN
|
||||
isc::log::ERROR
|
||||
isc::log::FATAL
|
||||
isc::log::NONE
|
||||
|
||||
b) The debug log level is only interpreted when the severity is
|
||||
DEBUG and is an integer ranging from 0 to 99. 0 should be used
|
||||
for the highest-level debug messages and 99 for the lowest-level
|
||||
(and typically more verbose) messages.
|
||||
|
||||
c) The external message file. If present, this is the same as a
|
||||
standard message file, although it should not include any
|
||||
directives. (A single directive of a particular type will be
|
||||
ignored; multiple directives will cause the read of the file to
|
||||
fail with an error.)
|
||||
|
||||
The settings remain in effect until the logging configuration is read,
|
||||
and so provide the default logging during program initialization.
|
||||
|
||||
3. Declare a logger through which the message will be logged.
|
||||
|
||||
isc::log::Logger logger("name");
|
||||
|
||||
The string passed to the constructor is the name of the logger (it
|
||||
can be any string) and is used when configuring it. Loggers with
|
||||
the same name share the same configuration.
|
||||
|
||||
4. Issue logging calls using supplied macros in "log/macros.h", e.g.
|
||||
|
||||
LOG_ERROR(logger, LOG_WRITE_ERROR).arg("output.txt");
|
||||
|
||||
(The macros are more efficient that calls to the methods on the logger
|
||||
class: they avoid the overhead of evaluating the parameters to arg()
|
||||
if the settings are such that the message is not going to be output.)
|
||||
|
||||
Using the Logging - Python
|
||||
==========================
|
||||
1. Build message module as describe above.
|
||||
|
||||
2. The main program unit must include a call to isc.log.init()
|
||||
(described in more detail below) to set the to set the logging
|
||||
severity, debug log level, and external message file:
|
||||
|
||||
a) The logging severity is one of the strings:
|
||||
|
||||
DEBUG
|
||||
INFO
|
||||
WARN
|
||||
ERROR
|
||||
FATAL
|
||||
NONE
|
||||
|
||||
b) The debug log level is only interpreted when the severity is
|
||||
DEBUG and is an integer ranging from 0 to 99. 0 should be used
|
||||
for the highest-level debug messages and 99 for the lowest-level
|
||||
(and typically more verbose) messages.
|
||||
|
||||
c) The external message file. If present, this is the same as a
|
||||
standard message file, although it should not include any
|
||||
directives. (Any that are there will be ignored.)
|
||||
|
||||
The settings remain in effect until the logging configuration is read,
|
||||
and so provide the default logging during program initialization.
|
||||
|
||||
3. Declare a logger through which the message will be logged.
|
||||
|
||||
isc.log.Logger logger("name")
|
||||
|
||||
The string passed to the constructor is the name of the logger (it
|
||||
can be any string) and is used when configuring it. Loggers with
|
||||
the same name share the same configuration.
|
||||
|
||||
4. Issue calls to the logging methods:
|
||||
|
||||
logger.error(LOG_WRITE_ERROR, "output.txt");
|
||||
|
||||
Logging Initialization
|
||||
======================
|
||||
In all cases, if an attempt is made to use a logging method before the logging
|
||||
has been initialized, the program will terminate with a LoggingNotInitialized
|
||||
exception.
|
||||
|
||||
C++
|
||||
---
|
||||
Logging Initialization is carried out by calling initLogger(). There are two
|
||||
variants to the call, one for use by production programs and one for use by
|
||||
unit tests.
|
||||
|
||||
Variant #1, Used by Production Programs
|
||||
---------------------------------------
|
||||
void isc::log::initLogger(const std::string& root,
|
||||
isc::log::Severity severity = isc::log::INFO,
|
||||
int dbglevel = 0, const char* file = NULL,
|
||||
bool buffer = false);
|
||||
|
||||
This is the call that should be used by production programs:
|
||||
|
||||
root
|
||||
Name of the program (e.g. "b10-auth"). This is also the name of the root
|
||||
logger and is used when configuring logging.
|
||||
|
||||
severity
|
||||
Default severity that the program will start logging with. Although this may
|
||||
be overridden when the program obtains its configuration from the configuration
|
||||
database, this is the severity that it used until then. (This may be set by
|
||||
a command-line parameter.)
|
||||
|
||||
dbglevel
|
||||
The debug level used if "severity" is set to isc::log::DEBUG.
|
||||
|
||||
file
|
||||
The name of a local message file. This will be read and its definitions used
|
||||
to replace the compiled-in text of the messages.
|
||||
|
||||
buffer
|
||||
If set to true, initial log messages will be internally buffered, until the
|
||||
first time a logger specification is processed. This way the program can
|
||||
use logging before even processing its logging configuration. As soon as any
|
||||
specification is processed (even an empty one), the buffered log messages will
|
||||
be flushed according to the specification. Note that if this option is used,
|
||||
the program SHOULD call one of the LoggerManager's process() calls (if you
|
||||
are using the built-in logging configuration handling in ModuleCCSession,
|
||||
this is automatically handled). If the program exits before this is done,
|
||||
all log messages are dumped in a raw format to stdout (so that no messages
|
||||
get lost).
|
||||
|
||||
Variant #2, Used by Unit Tests
|
||||
------------------------------
|
||||
void isc::log::initLogger()
|
||||
|
||||
This is the call that should be used by unit tests. In this variant, all the
|
||||
options are supplied by environment variables. (It should not be used for
|
||||
production programs to avoid the chance that the program operation is affected
|
||||
by inadvertently-defined environment variables.)
|
||||
|
||||
The environment variables are:
|
||||
|
||||
B10_LOGGER_ROOT
|
||||
Sets the "root" for the unit test. If not defined, the name "bind10" is used.
|
||||
|
||||
B10_LOGGER_SEVERITY
|
||||
The severity to set for the root logger in the unit test. Valid values are
|
||||
"DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE". If not defined, "INFO"
|
||||
is used.
|
||||
|
||||
B10_LOGGER_DBGLEVEL
|
||||
If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can be a
|
||||
number between 0 and 99, and defaults to 0.
|
||||
|
||||
B10_LOGGER_LOCALMSG
|
||||
If defined, points to a local message file. The default is not to use a local
|
||||
message file.
|
||||
|
||||
B10_LOGGER_DESTINATION
|
||||
The location to which log message are written. This can be one of:
|
||||
|
||||
stdout Message are written to stdout
|
||||
stderr Messages are written to stderr
|
||||
syslog[:facility] Messages are written to syslog. If the optional
|
||||
"facility" is used, the messages are written using
|
||||
that facility. (This defaults to "local0" if not
|
||||
specified.)
|
||||
Anything else Interpreted as the name of a file to which output
|
||||
is appended. If the file does not exist, a new one
|
||||
is opened.
|
||||
|
||||
In the case of "stdout", "stderr" and "syslog", they must be written exactly
|
||||
as is - no leading or trailing spaces, and in lower-case.
|
||||
|
||||
Python
|
||||
------
|
||||
To be supplied
|
||||
|
||||
|
||||
Severity Guidelines
|
||||
===================
|
||||
When using logging, the question arises, what severity should a message
|
||||
be logged at? The following is a suggestion - as always, the decision
|
||||
must be made in the context of which the message is logged.
|
||||
|
||||
One thing that should always be borne in mind is whether the logging
|
||||
could be used as a vector for a DOS attack. For example, if a warning
|
||||
message is logged every time an invalid packet is received, an attacker
|
||||
could simply send large numbers of invalid packets. (Of course, warnings
|
||||
could be disabled (or just warnings for that that particular logger),
|
||||
but nevertheless the message is an attack vector.)
|
||||
|
||||
FATAL
|
||||
-----
|
||||
The program has encountered an error that is so severe that it cannot
|
||||
continue (or there is no point in continuing). When a fatal error
|
||||
has been logged, the program will usually exit immediately (or shortly
|
||||
afterwards) after dumping some diagnostic information.
|
||||
|
||||
ERROR
|
||||
-----
|
||||
Something has happened such that the program can continue but the
|
||||
results for the current (or future) operations cannot be guaranteed to
|
||||
be correct, or the results will be correct but the service is impaired.
|
||||
For example, the program started but attempts to open one or more network
|
||||
interfaces failed.
|
||||
|
||||
WARN
|
||||
----
|
||||
An unusual event happened. Although the program will continue working
|
||||
normally, the event was sufficiently out of the ordinary to warrant
|
||||
drawing attention to it. For example, at program start-up a zone was
|
||||
loaded that contained no resource records,
|
||||
|
||||
INFO
|
||||
----
|
||||
A normal but significant event has occurred that should be recorded,
|
||||
e.g. the program has started or is just about to terminate, a new zone
|
||||
has been created, etc.
|
||||
|
||||
DEBUG
|
||||
-----
|
||||
This severity is only enabled on for debugging purposes. A debug level is
|
||||
associated with debug messages, level 0 (the default) being for high-level
|
||||
messages and level 99 (the maximum) for the lowest level. How the
|
||||
messages are distributed between the levels is up to the developer.
|
||||
So if debugging the NSAS (for example), a level 0 message might record
|
||||
the creation of a new zone, a level 10 recording a timeout when trying
|
||||
to get a nameserver address, but a level 50 would record every query for
|
||||
an address. (And we might add level 70 to record every update of the RTT.)
|
||||
|
||||
Note that like severities, levels are cumulative; so if level 25 is
|
||||
set as the debug level, all debug levels from 0 to 25 will be output.
|
||||
In fact, it is probably easier to visualise the debug levels as part of
|
||||
the severity system:
|
||||
|
||||
FATAL High
|
||||
ERROR
|
||||
WARN
|
||||
INFO
|
||||
DEBUG level 0
|
||||
DEBUG level 1
|
||||
:
|
||||
DEBUG level 99 Low
|
||||
|
||||
When a particular severity is set, it - and all severities and/or debug
|
||||
levels above it - will be logged.
|
||||
|
||||
To try to ensure that the information from different modules is roughly
|
||||
comparable for the same debug level, a set of standard debug levels has
|
||||
been defined for common type of debug output. However, modules are free
|
||||
to set their own debug levels or define additional ones.
|
||||
|
||||
Logging Sources v Logging Severities
|
||||
------------------------------------
|
||||
When logging events, make a distinction between events related to the
|
||||
server and events related to DNS messages received. Caution needs to
|
||||
be exercised with the latter as, if the logging is enabled in the normal
|
||||
course of events, such logging could be a denial of service vector. For
|
||||
example, suppose that the main authoritative service logger were to
|
||||
log both zone loading and unloading as INFO and a warning message if
|
||||
it received an invalid packet. An attacker could make the INFO messages
|
||||
unusable by flooding the server with malformed packets.
|
||||
|
||||
There are two approaches to get round this:
|
||||
|
||||
a) Make the logging of packet-dependent events a DEBUG-severity message.
|
||||
DEBUG is not enabled by default, so these events will not be recorded
|
||||
unless DEBUG is specifically chosen.
|
||||
|
||||
b) Record system-related and packet-related messages via different loggers
|
||||
(e.g. in the example given, server events could be logged using the
|
||||
logger "auth" and packet-related events at that level logged using the
|
||||
logger "pkt-auth".) As the loggers are independent and the severity
|
||||
levels independent, fine-tuning of what and what is not recorded can
|
||||
be achieved.
|
||||
|
||||
|
||||
Notes
|
||||
=====
|
||||
The message compiler is written in C++ (instead of Python) because it
|
||||
contains a component that reads the message file. This component is used
|
||||
in both the message compiler and the server; in the server it is used
|
||||
when the server starts up (or when triggered by a command) to read in
|
||||
a message file to overwrite the internal dictionary. Writing it in C++
|
||||
means there is only one piece of code that does this functionality.
|
762
src/lib/log/logging.dox
Normal file
762
src/lib/log/logging.dox
Normal file
@@ -0,0 +1,762 @@
|
||||
// Copyright (C) 2013-2014 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.
|
||||
|
||||
// Note: the prefix "log" to all labels is an abbreviation for "Logging"
|
||||
// and is used to prevent a clash with symbols in any other Doxygen file.
|
||||
|
||||
/**
|
||||
@page logBind10Logging BIND 10 Logging
|
||||
|
||||
@section logBasicIdeas Basic Ideas
|
||||
|
||||
The BIND 10 logging system is based on the log4J logging system
|
||||
common in Java development, and includes the following ideas:
|
||||
|
||||
- A set of severity levels.
|
||||
- A hierarchy of logging sources.
|
||||
- Separation of message use from message text.
|
||||
|
||||
@subsection logSeverity Message Severity
|
||||
Each message logged by BIND 10 has a severity associated with it, ranging
|
||||
from FATAL - the most severe - to DEBUG - messages output as the code
|
||||
executes to facilitate debugging. In order of decreasing severity,
|
||||
the levels are:
|
||||
|
||||
<dl>
|
||||
<dt>FATAL</dt>
|
||||
<dd>The program has encountered an error that is so severe that it
|
||||
cannot continue (or there is no point in continuing). For example, an
|
||||
unhandled exception generated deep within the code has been caught by the
|
||||
top-level program. When a fatal error has been logged, the program will
|
||||
exit immediately (or shortly afterwards) after dumping some diagnostic
|
||||
information.</dd>
|
||||
|
||||
<dt>ERROR</dt>
|
||||
<dd>Something has happened such that the program can continue but the
|
||||
results for the current (or future) operations cannot be guaranteed to
|
||||
be correct, or the results will be correct but the service is impaired.
|
||||
For example, the program started but attempts to open one or more network
|
||||
interfaces failed.</dd>
|
||||
|
||||
<dt>WARN</dt>
|
||||
<dd>(Warning) An unusual event happened. Although the program will
|
||||
continue working normally, the event was sufficiently out of the ordinary
|
||||
to warrant drawing attention to it. For example, the authoritative
|
||||
server loaded a zone that contained no resource records.</dd>
|
||||
|
||||
<dt>INFO</dt>
|
||||
<dd>(Information) A normal but significant event has occurred that should
|
||||
be recorded, e.g. the program has started or is just about to terminate,
|
||||
a new zone has been created, etc.</dd>
|
||||
|
||||
<dt>DEBUG</dt>
|
||||
<dd>Debug messages are output at this severity. Each message also
|
||||
has a debug level associated with it, ranging from 0 (the default)
|
||||
for high-level messages and level 99 (the maximum) for the the lowest
|
||||
level.</dd>
|
||||
</dl>
|
||||
|
||||
When logging is enabled for a component, it is enabled for a particular
|
||||
severity level and all higher severities. So if logging is enabled
|
||||
for INFO messages, WARN, ERROR and FATAL messages will also be logged,
|
||||
but not DEBUG ones. If enabled for ERROR, only ERROR and FATAL messages
|
||||
will be logged.
|
||||
|
||||
As noted above, DEBUG messages are also associated with a debug level.
|
||||
This allows the developer to control the amount of debugging information
|
||||
produced; the higher the debug level, the more information is output.
|
||||
For example, if debugging the NSAS (nameserver address store), debug
|
||||
levels might be assigned as follows: a level 0 debug message records
|
||||
the creation of a new zone, a level 10 logs every timeout when trying
|
||||
to get a nameserver address, a level of 50 records every query for an
|
||||
address and a level of 70 records every update of the round-trip time.
|
||||
|
||||
Like severities, levels are cumulative; so if level 25 is set as the
|
||||
debug level, all debug messages associated levels 0 to 25 (inclusive)
|
||||
will be output. In fact, it is probably easier to visualise the debug
|
||||
levels as part of the severity system:
|
||||
@code
|
||||
FATAL Most severe
|
||||
ERROR
|
||||
WARN
|
||||
INFO
|
||||
DEBUG level 0
|
||||
DEBUG level 1
|
||||
:
|
||||
DEBUG level 99 Least severe
|
||||
@endcode
|
||||
When a particular debug level is set, it - and all debug levels and
|
||||
severities above it - will be logged.
|
||||
|
||||
To try to ensure that the information from different modules is roughly
|
||||
comparable for the same debug level, a set of standard debug levels has
|
||||
been defined for common types of debug output. (These can be found in
|
||||
@ref log_dbglevels.h) However, modules are free to set their own debug
|
||||
levels or define additional ones.
|
||||
|
||||
@subsection logHierarchical Hierarchical Logging System
|
||||
|
||||
When a program writes a message to the logging system, it does so using an
|
||||
instance of the @ref isc::log::Logger class. As well as performing the
|
||||
write of the message, the logger identifies the source of the message:
|
||||
different sources can write to different destinations and can log
|
||||
different severities of messages. For example, the logger associated
|
||||
with the resolver's cache code could write debugging and other messages
|
||||
to a file while all other components only write messages relating to
|
||||
errors to the syslog file.
|
||||
|
||||
The loggers are hierarchical in that each logger is the child of another
|
||||
logger. The top of the hierarchy is the root logger; this does not
|
||||
have a parent. The reason for this hierarchy is that unless a logger
|
||||
explicitly assigns a value to an attribute (such as severity of messages
|
||||
it should log), it picks it up the value from the parent. In BIND 10,
|
||||
each component (b10-auth, b10-resolver etc.) has a root logger (named
|
||||
after the program) and every other logger in the component is a child
|
||||
of that. So in the example above, the error/syslog attributes could be
|
||||
associated with the b10-resolver logger while the logger associated with
|
||||
the cache sets its own values for the debug/file attributes.
|
||||
|
||||
More information about the logging hierarchy can be found in the section
|
||||
on Logging configuration in the <a
|
||||
href="http://bind10.isc.org/docs/bind10-guide.html#logging">BIND 10
|
||||
Guide</a>.
|
||||
|
||||
@subsection logSeparationUseText Separation of Messages Use from Message Text
|
||||
|
||||
Separating the use of the message from the text associated with it -
|
||||
in essence, defining message text in an external file - allows for the
|
||||
replacement the supplied text of the messages with a local language version.
|
||||
It also means that other attributes can be associated with the message,
|
||||
for example, an explanation of the meaning of the message and other
|
||||
information such as remedial action in the case of errors.
|
||||
|
||||
Each message has an identifier such as "LOG_WRITE_ERROR".
|
||||
Within the program, this is the symbol passed to the logging system
|
||||
which uses the symbol as an index into a dictionary to retrieve the message
|
||||
associated with it (e.g. "unable to open %1 for input"), after which it
|
||||
substitutes any message parameters (in this example, the name of the file
|
||||
where the write operation failed) and logs the result to the destination.
|
||||
|
||||
In BIND 10, a the default text for each message is linked into the
|
||||
program. Each program is able to read a locally-defined message file
|
||||
when it starts, updating the stored definitions with site-specific text.
|
||||
When the message is logged, the updated text is output. However, the
|
||||
message identifier is always included in the output so that the origin
|
||||
of the message can be identified even if the text has been changed.
|
||||
|
||||
@note Local message files have not yet been implemented in BIND 10.
|
||||
|
||||
|
||||
|
||||
@section logDeveloperUse Using Logging in a BIND 10 Component
|
||||
|
||||
The steps in using the logging system in a BIND 10 component (such as
|
||||
an executable or library) are:
|
||||
|
||||
<ol>
|
||||
<li>Create a message file. This defines messages by an identification
|
||||
string and and text that explains the message in more detail. Ideally the
|
||||
file should have a file type of ".mes".</li>
|
||||
|
||||
<li>Run it through the message compiler to produce the files for your
|
||||
module. This step should be included in the build process. The message
|
||||
compiler is a BIND10 program and is one of the first programs built and
|
||||
linked in the build process. As a result, it should be available for
|
||||
compiling the message files of all BIND 10 components and libraries.
|
||||
|
||||
For C++ development, the message compiler produces two files in the
|
||||
default directory, having the same name as the input file but with file
|
||||
types of ".h" and ".cc". For Python, the message compiler will produce
|
||||
a Python module containing the symbols.</li>
|
||||
|
||||
<li>Include the resultant files in your source code to define message symbols,
|
||||
and (for C++) compile the code and link it into your program.</li>
|
||||
|
||||
<li>Declare loggers in your code and use them to log messages.</li>
|
||||
|
||||
<li>Call the logger initialization function in the program's main module.</li>
|
||||
|
||||
</ol>
|
||||
|
||||
The following sections describe these steps in more detail.
|
||||
|
||||
|
||||
@subsection logMessageFiles Create a Message File
|
||||
|
||||
A message file contains message definitions. Typically there
|
||||
will be one message file for each component that uses BIND 10 logging.
|
||||
An example file could be:
|
||||
|
||||
@code
|
||||
# Example message file
|
||||
|
||||
$NAMESPACE isc::log
|
||||
|
||||
% LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'
|
||||
A line starting with a dollar symbol was found, but the first word on the line
|
||||
(shown in the message) was not a recognised message compiler directive.
|
||||
|
||||
% LOG_WRITE_ERROR error writing to %1: %2
|
||||
The specified error was encountered by the message compiler when writing to
|
||||
the named output file.
|
||||
@endcode
|
||||
|
||||
Points to note are:
|
||||
|
||||
<ul>
|
||||
<li>Leading and trailing spaces are trimmed from each line before it
|
||||
is processed. Although the above example has every line starting at
|
||||
column 1, the lines could be indented if desired.</li>
|
||||
|
||||
<li>Lines starting with "#" are comments are are ignored. Comments must
|
||||
be on a line by themselves; inline comments will be interpreted as part
|
||||
of the text of that line.</li>
|
||||
|
||||
<li>Lines starting with "$" are directives. At present, just one
|
||||
directive is recognised:
|
||||
<dl>
|
||||
<dt>$NAMESPACE <namespace-name></dt>
|
||||
<dd>The sole argument is the name of the namespace in which the
|
||||
symbols are created. In the absence of a $NAMESPACE directive,
|
||||
symbols will be put in the anonymous namespace.</dd>
|
||||
</dl>
|
||||
</li>
|
||||
|
||||
<li>Lines starting with "%" are message definitions and comprise the message
|
||||
identification and the message text. For example:
|
||||
@code
|
||||
% LOG_WRITE_ERROR error writing to %1: %2
|
||||
@endcode
|
||||
|
||||
There may be zero or more spaces between the leading "%" and the
|
||||
message identification (which, in the example above, is the string
|
||||
"LOG_WRITE_ERROR").</li>
|
||||
|
||||
<li>The message identification can be any string of letters, digits and
|
||||
underscores, but must not start with a digit.</li>
|
||||
|
||||
<li>The rest of the line - from the first non-space character to the
|
||||
last non- space character - is the text of the message. There are no
|
||||
restrictions on what characters may be in this text, other than they be
|
||||
printable (so both single-quote (') and double-quote (") characters are
|
||||
allowed). The message text may include replacement tokens (the strings
|
||||
"%1", "%2" etc.). When a message is logged, these are replaced with the
|
||||
arguments passed to the logging call: %1 refers to the first argument,
|
||||
%2 to the second etc. Within the message text, the placeholders can
|
||||
appear in any order and placeholders can be repeated. Otherwise, the
|
||||
message is printed unmodified.</li>
|
||||
|
||||
<li>Remaining lines indicate an explanation for the preceding message.
|
||||
The explanation can comprise multiple paragraphs, the paragraphs being
|
||||
separated by blank lines. These lines are intended to be processed by a
|
||||
separate program to generate an error messages manual; they are ignored
|
||||
by the message compiler.</li>
|
||||
|
||||
<li>Except when used to separate paragraphs in the message explanation,
|
||||
blank lines are ignored.</li>
|
||||
</ul>
|
||||
|
||||
Although there are few restriction on what can be in the message
|
||||
identifcation and text, there are a number of conventions used by BIND
|
||||
10, both in the contents of the message and in the usage. All code
|
||||
should adhere to these:
|
||||
|
||||
<ul>
|
||||
<li>Message identifications should include at least one underscore.
|
||||
The component before the first underscore is a string indicating the
|
||||
origin of the message, and the remainder describes the condition.
|
||||
So in the example above, the LOG indicates that the error originated
|
||||
from the logging library and the "WRITE_ERROR" indicates that there was
|
||||
a problem in a write operation.</li>
|
||||
|
||||
<li>The part of the message identification describing the error (e.g.
|
||||
"WRITE_ERROR" in the example above) should comprise no more than
|
||||
two or three words separated by underscores. An excessive number
|
||||
of words or overly long message identification should be avoided;
|
||||
such information should be put in the text of the message. For example,
|
||||
"RESOLVER_ERROR_FETCHING_NAME_FROM_UPSTREAM_SERVER" is excessively long,
|
||||
"RESOLVER_FETCH_ERROR" being better.</li>
|
||||
|
||||
<li>Similarly, the text of the message should be reasonably concise. It should
|
||||
include enough information (possibly supplied at run-time in the form of
|
||||
parameters) to allow further investigations to be undertaken if required.
|
||||
|
||||
Taking the above example, a suitable error message to indicate that the
|
||||
resolver has failed to read a name from an upstream authoritative server
|
||||
could be:
|
||||
|
||||
@code
|
||||
% RESOLVER_FETCH_ERROR fetch from %1 failed, error code %2 (%3)
|
||||
@endcode
|
||||
|
||||
... where %1 indicates the name or IP address of the server to which the
|
||||
fetch was sent, %2 the errno value returned and %3 the message associated
|
||||
with that error number (retrieved via a call to "strerror()").
|
||||
|
||||
</li>
|
||||
|
||||
<li>The message should not have a comma after the message identification.
|
||||
The message text should neither start with a capital letter (unless
|
||||
the first word is a proper noun or is normally written in capitals)
|
||||
nor end with a period. The message reporting system takes care of such
|
||||
punctuation.</li>
|
||||
|
||||
<li>The parameters substituted into the message text should not include
|
||||
line breaks. Messages are normally output to the syslog file which
|
||||
has the inbuilt assumption of one line per message. Splitting a message
|
||||
across multiple lines makes it awkward to search the file for messages
|
||||
and associated information.</li>
|
||||
|
||||
<li>The message identifier should be unique across the entire BIND 10
|
||||
system. (An error will be reported at system start-up if an identifier
|
||||
is repeated.)</li>
|
||||
|
||||
<li>A particular message identifier should only be used at one place in
|
||||
the BIND 10 code. In this way, if the message indicates a problem, the
|
||||
code in question can be quickly identified.</li>
|
||||
|
||||
<li>The explanation of the message - the free-form text following the
|
||||
message identification - appears in the BIND 10 message manual. It
|
||||
should:
|
||||
|
||||
<ul>
|
||||
<li>Describe the serverity of the message (debug, informational etc.)</li>
|
||||
|
||||
<li>Expand on the text of the message. In some cases, such as
|
||||
debug messages, the message text may provide more or less sufficient
|
||||
description. For warnings and errors, the explanation should provide
|
||||
sufficient background to the problem to allow a non-developer to
|
||||
understand the issue and to begin fault-finding. If possible, the
|
||||
explanation should also include suggested remedial action.</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@subsection logSourceFiles Produce Source Files
|
||||
The message file created in the previous step is then run through the
|
||||
message compiler to produce source files that are included in the BIND
|
||||
10 programs.
|
||||
|
||||
@subsubsection logMessageCompiler Message Compiler
|
||||
The message compiler is a program built in the src/log/compiler directory.
|
||||
It is invoked by the command:
|
||||
@code
|
||||
message [-h] [-v] [-p] [-d dir] <message-file>
|
||||
@endcode
|
||||
"-v" prints the version number and exits; "-h" prints brief help text.
|
||||
The compiler produces source files for C++ unless the "-p" switch is
|
||||
specified, in which case it produces Python code. Finally, the "-d"
|
||||
switch directs the compiler to produce the output files in the specified
|
||||
directory (the default being the current working directory).
|
||||
|
||||
<b>C++ Files</b><br/>
|
||||
Without the "-p" option, the message compiler processes the message file
|
||||
to produce two files:
|
||||
|
||||
<ol>
|
||||
<li>A C++ header file (called <message-file-name>.h) holding lines of
|
||||
the form:
|
||||
@code
|
||||
namespace <namespace-name> {
|
||||
extern const isc::log::MessageID LOG_BAD_DESTINATION;
|
||||
extern const isc::log::MessageID LOG_BAD_SEVERITY;
|
||||
:
|
||||
}
|
||||
@endcode
|
||||
The symbols define keys in the global message dictionary, with
|
||||
the namespace enclosing the symbols set by the $NAMESPACE directive.
|
||||
(This is the reason for the restriction on message identifiers - they
|
||||
have to be valid C++ symbol names.)</li>
|
||||
|
||||
<li>A C++ source file (called <message-file-name>.cc) that holds the definitions
|
||||
of the global symbols and code to insert the symbols and messages into
|
||||
an internal dictionary.
|
||||
|
||||
Symbols are defined to be equal to strings equal to the identifier, e.g.
|
||||
@code
|
||||
extern const isc::log::MessageID LOG_BAD_DESTINATION = "LOG_BAD_DESTINATION";
|
||||
extern const isc::log::MessageID LOG_BAD_SEVERITY = "LOG_BAD_SEVERITY";
|
||||
:
|
||||
@endcode
|
||||
(The current implementation allows symbols to be compared. However,
|
||||
use of strings should not be assumed - a future implementation may change
|
||||
this.) In addition, the file declares an array of identifiers/messages
|
||||
and an object to add them to the global dictionary, e.g.:
|
||||
@code
|
||||
namespace {
|
||||
const char* values[] = {
|
||||
"LOG_BAD_DESTINATION", "unrecognized log destination: %1",
|
||||
"LOG_BAD_SEVERITY", "unrecognized log severity: %1",
|
||||
:
|
||||
NULL
|
||||
};
|
||||
const isc::log::MessageInitializer initializer(values);
|
||||
}
|
||||
@endcode
|
||||
|
||||
The constructor of the @ref isc::log::MessageInitializer object retrieves
|
||||
the singleton global @ref isc::log::MessageDictionary object (created
|
||||
using standard methods to avoid the "static initialization fiasco") and
|
||||
adds each identifier and associated text to it. These constructors are run
|
||||
when the program starts; a check is made as each identifier is added and,
|
||||
if the identifier already exists in the dictionary, a warning message
|
||||
is printed to the main logging output when logging is finally enabled.
|
||||
The appearance of such a message indicates a programming error.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<b>Python Files</b><br/>
|
||||
If the "-p" option is given, the compiler produces a Python module defining
|
||||
the messages. The content of this is of the form:
|
||||
@code
|
||||
import isc.log
|
||||
:
|
||||
LOG_WRITE_ERROR = isc.log.create_message("LOG_WRITE_ERROR",
|
||||
"error writing to %1 : %2")
|
||||
@endcode
|
||||
(The definition is output on one line - it is split across two lines in this
|
||||
document for readability.)
|
||||
|
||||
The module can be imported into other Python code, and messages logged
|
||||
in a similar way to C++ using the Python logging library.
|
||||
|
||||
@subsubsection logMakefile Include Message Compilation in Makefile
|
||||
The source file for the messages is the ".mes" file, but the files used
|
||||
by the code (which, in the case of C++, must be compiled and linked)
|
||||
are the output of the message compiler. (The compiler is produced very
|
||||
early on in the BIND 10 build sequence, so is available for use in the
|
||||
building of subsequent components.) To allow this, certain dependencies
|
||||
must be included in the Makefile.am for each component that uses logging.
|
||||
|
||||
<b>Including Message files in C++ Component Builds</b><br/>
|
||||
The following segment from the "hooks" Makefile.am illustrates
|
||||
the entries needed.
|
||||
@code
|
||||
# Define rule to build logging source files from message file
|
||||
hooks_messages.h hooks_messages.cc: s-messages
|
||||
|
||||
s-messages: hooks_messages.mes
|
||||
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
|
||||
touch $@
|
||||
|
||||
# Tell automake that the message files are built as part of the build process
|
||||
# (so that they are built before the main library is built).
|
||||
BUILT_SOURCES = hooks_messages.h hooks_messages.cc
|
||||
|
||||
# Ensure that the message file is included in the distribution
|
||||
EXTRA_DIST = hooks_messages.mes
|
||||
|
||||
# Get rid of generated message files on a clean
|
||||
CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc s-messages
|
||||
@endcode
|
||||
The first two rules relate the output .h and .cc files produced by the
|
||||
message compiler to the input .mes file. The intermediate "s-messages"
|
||||
file is used to overcome synchronization issues with parallel builds
|
||||
(where "make" uses multiple processes running in parallel). Note that the
|
||||
reference to both the compiler and the input message file are via absolute
|
||||
paths defined in terms of Automake macros. In particular it is important
|
||||
that the message compiler - which is created during the build process - is
|
||||
referred to via the "top_builddir" macro, whereas the input message file -
|
||||
which is in the repository - is accessed through the "top_srcdir" macro.
|
||||
|
||||
The BUILT_SOURCES line notifies the Automake that the .h and .cc files
|
||||
need to be created before they can be used in the compilation, so
|
||||
instructs it to organse things so that the message compiler is run first.
|
||||
|
||||
As the .mes file is not directly included in any compilation, it will
|
||||
not be automatically copied into a distribution created through this
|
||||
Makefile.am. The EXTRA_DIST line informs Automake that this file does
|
||||
need to be included.
|
||||
|
||||
Finally, the intermediate files - the .cc and .h file, as well as the
|
||||
intermediate s-messages file - need to be removed when "make clean" is run.
|
||||
These files are therefore included in the definition of the CLEANFILES macro.
|
||||
|
||||
Not shown are the Makefile.am lines where the .h and .cc file are used. These
|
||||
are the same as other lines specifying .h and .cc source files.
|
||||
|
||||
<b>Including Message files in Python Component Builds</b><br/>
|
||||
The following (modified) segments from the "xfrin" Makefile.am illustrates
|
||||
the entries needed.
|
||||
|
||||
@code
|
||||
CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py
|
||||
CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.pyc
|
||||
:
|
||||
EXTRA_DIST += xfrin_messages.mes
|
||||
:
|
||||
# Define rule to build logging source files from message file
|
||||
$(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py : xfrin_messages.mes
|
||||
$(top_builddir)/src/lib/log/compiler/message \
|
||||
-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrin_messages.mes
|
||||
@endcode
|
||||
The CLEANFILES lines remove the created Python (and compiled Python)
|
||||
code when "make clean" is run.
|
||||
|
||||
The EXTRA_DIST line ensures that the .mes file is copied to the
|
||||
distribution tarball.
|
||||
|
||||
The final dependency shows the use of the message compiler to
|
||||
create the Python message module in a working directory.
|
||||
|
||||
|
||||
@subsection logUsage Using Logging Files in Program Development
|
||||
|
||||
@subsubsection logCppUsage Use in a C++ Program or Module
|
||||
To use logging in a C++ program or module:
|
||||
|
||||
<ol>
|
||||
<li>Build the message header file and source file as described above.</li>
|
||||
|
||||
<li>In each C++ file in which logging is to be used, declare a logger
|
||||
through which the message will be logged.
|
||||
|
||||
@code
|
||||
isc::log::Logger logger("name");
|
||||
@endcode
|
||||
This declaration can be per-function, or it can be declared statically
|
||||
in file scope. The string passed to the constructor is the name of
|
||||
the logger (it can be any string) and is used when configuring it.
|
||||
(Remember though that the name of root logger for the program will be
|
||||
prepended to the name chosen. So if, for example, the name "cache"
|
||||
is chosen and the model is included in the "b10-resolver" program, the
|
||||
full name of the logger will be "b10-resolver.cache".) Loggers with
|
||||
the same name share the same configuration. For this reason if, as is
|
||||
usual, messages logged in different files in the same component (e.g.
|
||||
hooks module, nameserver address store, etc.) originate from loggers
|
||||
with the same name, the logger declaration can be placed into a header
|
||||
file.</li>
|
||||
|
||||
<li>Issue logging calls using supplied macros in "log/macros.h", e.g.
|
||||
@code
|
||||
LOG_ERROR(logger, LOG_WRITE_ERROR).arg("output.txt");
|
||||
LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_LOOKUP_CANCEL).arg(zone);
|
||||
@endcode
|
||||
All macros (with the exception of LOG_DEBUG) take two arguments:
|
||||
the C++ logger object that will be used to log the message, and the
|
||||
identification of the message to be logged. LOG_DEBUG takes three
|
||||
arguments, the additional one being the debug level associated with
|
||||
the message. The .arg() call appended to the end of the LOG_XXX()
|
||||
macro handles the arguments to the message. A chain of these is used
|
||||
in cases where a message takes multiple arguments, e.g.
|
||||
@code
|
||||
LOG_DEBUG(nsas_logger, NSAS_DBG_RTT, NSAS_UPDATE_RTT)
|
||||
.arg(addresses_[family][index].getAddress().toText())
|
||||
.arg(old_rtt).arg(new_rtt);
|
||||
@endcode
|
||||
Using the macros is more efficient than direct calls to the methods on
|
||||
the logger class: they avoid the overhead of evaluating the parameters
|
||||
to arg() if the logging settings are such that the message is not going
|
||||
to be output (e.g. it is a DEBUG message and the logging is set to output
|
||||
messages of INFO severity or above).</li>
|
||||
|
||||
<li>The main program unit must include a call to isc::log::initLogger()
|
||||
(described in more detail below) to set the initial logging severity, debug log
|
||||
level, and external message file.
|
||||
</ol>
|
||||
|
||||
@subsubsection logPythonUsage Use in a Python Module
|
||||
To use logging in a Python module:
|
||||
<ol>
|
||||
<li>Build message module as described above.</li>
|
||||
|
||||
<li>Declare a logger through which the message will be logged.
|
||||
@code
|
||||
isc.log.Logger logger("name")
|
||||
@endcode
|
||||
The string passed to the constructor is the name of the logger (it can
|
||||
be any string) and is used when configuring it. Loggers with the same
|
||||
name share the same configuration.</li>
|
||||
|
||||
<li>Issue calls to the logging methods:
|
||||
@code
|
||||
logger.error(LOG_WRITE_ERROR, "output.txt")
|
||||
@endcode
|
||||
The message parameters are included as trailing arguments in the
|
||||
logger call.</li>
|
||||
|
||||
<li>The main program unit must include a call to isc.log.init() (described
|
||||
in more detail below) to set the to set the logging severity, debug log
|
||||
level, and external message file. The settings remain in effect until
|
||||
the logging configuration is read, so are the ones used during program
|
||||
initialization.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
@subsection logInitialization Logging Initialization
|
||||
In all cases, if an attempt is made to use a logging method before
|
||||
the logging has been initialized, the program will terminate with a
|
||||
LoggingNotInitialized exception.
|
||||
|
||||
@subsection logInitializationCpp C++ Initialization
|
||||
Logging Initialization is carried out by calling @ref
|
||||
isc::log::initLogger(). There are two variants to the call, one for
|
||||
use by production programs and one for use by unit tests.
|
||||
|
||||
@subsubsection logInitializationCppVariant1 Variant #1, Used by Production Programs
|
||||
The call that should be used by all production programs is:
|
||||
@code
|
||||
void isc::log::initLogger(const std::string& root,
|
||||
isc::log::Severity severity = isc::log::INFO,
|
||||
int dbglevel = 0, const char* file = NULL,
|
||||
bool buffer = false);
|
||||
@endcode
|
||||
Arguments are:
|
||||
<dl>
|
||||
<dt><code>root</code></dt>
|
||||
<dd>Name of the root logger. This should be the name of the program
|
||||
(e.g. "b10-auth") and is used when configuring logging.</dd>
|
||||
|
||||
<dt><code>severity</code></dt>
|
||||
<dd>Default severity that the program will start logging with. Although
|
||||
this may be overridden when the program obtains its configuration from
|
||||
the configuration database, this is the severity that it used until then.
|
||||
The logging severity is one of the enum defined in @ref logger.h, i.e.
|
||||
|
||||
@code
|
||||
isc::log::DEBUG
|
||||
isc::log::INFO
|
||||
isc::log::WARN
|
||||
isc::log::ERROR
|
||||
isc::log::FATAL
|
||||
isc::log::NONE
|
||||
@endcode
|
||||
|
||||
(The level NONE may be used to disable logging.)
|
||||
</dd>
|
||||
|
||||
<dt><code>dbglevel</code></dt>
|
||||
<dd>The debug log level is only interpreted when the severity is
|
||||
isc::log::DEBUG and is an integer ranging from 0 to 99. 0 should be
|
||||
used for the highest-level debug messages and 99 for the lowest-level
|
||||
(and typically more verbose) messages.</dd>
|
||||
|
||||
<dt><code>file</code></dt>
|
||||
<dd>The name of a local message file. This will be read and its
|
||||
definitions used to replace the compiled-in text of the messages.
|
||||
The default value of NULL indicates that no local message file is
|
||||
supplied.</dd>
|
||||
|
||||
<dt><code>buffer</code></dt>
|
||||
<dd>If set to true, initial log messages will be internally buffered,
|
||||
until the first time a logger specification is processed. This
|
||||
way the program can use logging before even processing its logging
|
||||
configuration. As soon as any specification is processed (even an
|
||||
empty one), the buffered log messages will be flushed according to
|
||||
the specification. Note that if this option is used, the program
|
||||
SHOULD call one of the @ref isc::log::LoggerManager::process() calls.
|
||||
(If you are using the built-in logging configuration handling in @ref
|
||||
isc::config::ModuleCCSession, this is automatically handled.) If the
|
||||
program exits before this is done, all log messages are dumped in a raw
|
||||
format to stdout (so that no messages get lost).</dd>
|
||||
</dl>
|
||||
|
||||
@subsubsection logInitializationCppVariant2 Variant #2, Used by Unit Tests
|
||||
@code
|
||||
void isc::log::initLogger()
|
||||
@endcode
|
||||
This is the call that should be used by unit tests. In this variant,
|
||||
all the options are supplied by environment variables: it should not
|
||||
be used for production programs to avoid the chance that the program
|
||||
operation is affected by inadvertently-defined environment variables. The
|
||||
environment variables are:
|
||||
|
||||
<dl>
|
||||
<dt>B10_LOGGER_ROOT</dt>
|
||||
<dd>Sets the "root" for the unit test. If not defined, the name "bind10"
|
||||
is used.</dd>
|
||||
|
||||
<dt>B10_LOGGER_SEVERITY</dt>
|
||||
<dd>The severity to set for the root logger in the unit test.
|
||||
Valid values are "DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE".
|
||||
If not defined, "INFO" is used.</dd>
|
||||
|
||||
<dt>B10_LOGGER_DBGLEVEL</dt>
|
||||
<dd>If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can
|
||||
be a number between 0 and 99, and defaults to 0.</dd>
|
||||
|
||||
<dt>B10_LOGGER_LOCALMSG</dt>
|
||||
<dd>If defined, points to a local message file. The default is not to
|
||||
use a local message file.</dd>
|
||||
|
||||
<dt>B10_LOGGER_DESTINATION</dt>
|
||||
<dd>The location to which log message are written. This can be one of:
|
||||
<ul>
|
||||
<li><b>stdout</b> Message are written to stdout.</li>
|
||||
<li><b>stderr</b> Messages are written to stderr.</li>
|
||||
<li><b>syslog[:facility]</b> Messages are written to syslog. If the
|
||||
optional "facility" is used, the messages are written using that facility.
|
||||
(This defaults to "local0" if not specified.)</li>
|
||||
<li><b>Anything else</b> Interpreted as the name of a file to which
|
||||
output is appended. If the file does not exist, a new one is opened.</li>
|
||||
</ul>
|
||||
In the case of "stdout", "stderr" and "syslog", they must be written exactly
|
||||
as is - no leading or trailing spaces, and in lower-case.</dd>
|
||||
</dl>
|
||||
|
||||
@subsection logInitializationPython Python Initialization
|
||||
To initialize the logger in a Python program, the "init" method must be
|
||||
called:
|
||||
@code
|
||||
isc.log.init(name, severity, debuglevel, file, buffer)
|
||||
@endcode
|
||||
|
||||
<dl>
|
||||
<dt><code>name</code></dt>
|
||||
<dd>String giving the name of the root logger. This is the only mandatory
|
||||
argument, the rest are optional.</dd>
|
||||
|
||||
<dt><code>severity</code></dt>
|
||||
<dd>The severity, and is one of the strings "DEBUG", INFO" etc.
|
||||
The default is "INFO".</dd>
|
||||
|
||||
<dt><code>debuglevel</code></dt>
|
||||
<dd>Debug level, an integer between 0 and 99. A default value of 0 will
|
||||
be used if this is not specified.</dd>
|
||||
|
||||
<dt><code>file</code></dt>
|
||||
<dd>Name of the external message file (if present). By default, no
|
||||
external message file is used.</dd>
|
||||
|
||||
<dt><code>buffer</code></dt>
|
||||
<dd>If set to true, initial log messages will be internally buffered,
|
||||
until the first time a logger specification is processed. This
|
||||
way the program can use logging before even processing its logging
|
||||
configuration. By default, no buffer is used.</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
@section logNotes Notes on the Use of Logging
|
||||
One thing that should always be kept in mind is whether the logging
|
||||
could be used as a means for a DOS attack. For example, if a warning
|
||||
message is logged every time an invalid packet is received, an attacker
|
||||
could simply send large numbers of invalid packets. Of course, warnings
|
||||
could be disabled (or just warnings for that that particular logger),
|
||||
but nevertheless the message is an attack vector. As a general rule,
|
||||
if the message can be triggered by a user action, it can be used as an
|
||||
attack vector.
|
||||
|
||||
There are two approaches to get round this:
|
||||
<ol>
|
||||
<li>Log messages generated by such user actions as DEBUG messages. DEBUG
|
||||
is not enabled by default, so these events will not be recorded unless
|
||||
DEBUG is specifically enabled. Choosing a suitable debug level for
|
||||
such messages will select only those messages and not the more general
|
||||
debug messages.</li>
|
||||
|
||||
<li>Record system-related and packet-related messages via different
|
||||
loggers. As the loggers are independent and the severity levels
|
||||
independent, fine-tuning of what and what is not recorded can be achieved.</li>
|
||||
|
||||
</ol>
|
||||
*/
|
Reference in New Issue
Block a user