The SENF Logging library

The Loggger infrastructure implements a highly flexible compile- and run-time configurable logging infrastructure supporting multiple streams, user definable log areas and fine grained log levels. Logging can be configured at compile and runtime on any combination of above parameters. The library supports a host of log targets and messages can be routed into multiple targets at the same time. To allow concise usage of the library, a utility to define logging defaults for any scope is provided.

See also
Logging commands
Configuration
Targets
Log levels

Concepts

Log messages are arbitrarily created throughout the code using simple log statements (which are
macros). Besides the log message itself, every log message is labeled with additional
information: The \e stream, the \e area and a log \e level. If the message is not compile-time
disabled, the message is then directed to one or several log \e targets.

A \e stream combines log messages with a single purpose: Debug messages, access logging and so
on. Any number of streams may be defined. There is one predefined default stream called \c
senf::log::Debug. (see: \ref SENF_LOG_DEFINE_STREAM)

The \e area gives information about the source location of the message. Areas may be defined and
assigned arbitrarily but should be used to label messages from a single class or subsystem. It
is possible to reuse a class as it's own area tag, which is often desirable.  There is a
default area \c senf::log::DefaultArea which is used, when no other area is assigned. (see: \ref
SENF_LOG_DEFINE_AREA, \ref SENF_LOG_CLASS_AREA)

The log \e level gives information on the importance of the message. The list of log-levels is
fixed. (see: \ref loglevels)

Depending on their the \e stream, \e area and \e level information, log messages can be enabled
or disabled at \e compile time. Messages disabled at compile time should not generate any
code. (see: \ref SENF_LOG_CONF)

\attention The default log stream senf::log::Debug has senf::log::VERBOSE messages
    <em>disabled</em> at compile time. senf::log::VERBOSE message will therefore only appear,
    if you explicitly enable the messages for the area in question using (here for the area
    <code>some::Area</code>)
g++ ... -DSENF_LOG_CONF="(( (senf)(log)(Debug), (some)(Area), VERBOSE ))"

in addition to routing the messages at runtime. For more, see Configuration.

To be of any use, the log messages have to be written somewhere. This is the responsibility of any number of targets. A target receives messages and using it's routing information decides, wether the message is output or not. A message may be routed to multiple targets simultaneously or may not be output by any target at all. (see: Targets)

Tutorial introduction

Using the logging library mostly concerns using \ref SENF_LOG statements in your code. There are
some other helpers used to simplify specifying parameters.
namespace foo {
// Define a new log stream with default level, runtime limit and compile time limit
// set to senf::log::MESSAGE
class Froblizer
{
// Define a log area which will automatically be used by all members of this class.
// This is a combination of SENF_LOG_DEFINE_AREA and SENF_LOG_DEFAULT_AREA.
// Set default log parameters for this scope.
SENF_LOG_DEFAULT_STREAM(foo::UserLog);
// Define an alias for emergency debug messages
// The log area is inherited from the default at the place, where this
// alias is used *not* where it is defined
SENF_LOG_DEFINE_ALIAS(LogEmerg, (senf::log::Debug)(senf::log::CRITICAL));
void test();
public:
void froblize();
};
}
void foo::Froblizer::froblize()
{
SENF_LOG(("This is the UserLog at level NOTICE in the FroblizeArea"));
SENF_LOG((senf::log::IMPORTANT) ("Same stream and area but at important level"));
SENF_LOG((LogEmerg) ("This goes to the DebugLog at level CRITICAL in the FroblizerArea"));
}
void foo::Froblizer::test()
{
// Change the default log level for this method. stream and area are taken
// from the next scope up
SENF_LOG(("Log to UserLog stream in Froblizer area however at VERBOSE level"));
}
int main(int, char **)
{
// Set up the routing targets
senf::log::FileTarget logfile ("my.log");
// Debug messages go to the console
console.route<senf::log::Debug>();
// Important user message are written to the log file
logfile.route<foo::UserLog, senf::log::IMPORTANT>();
}
\par "Implementation note:" I would have much preferred a more C++ like implementation. However given the
    design goals
    \li Flexible configuration at compile and runtime
    \li Concise usage and simple interface
    \li Zero overhead for compile-time disabled log messages

    I did not find any non-mcaro implementation which was not either completely convoluted,
    unusable or slow. So I turned to a macro based implementation which can provide all the
    design goals stated above.