Introduction
I would like to discuss about Log4Cplus is an opensource component for logging purspose used in C++ applications. Logging is an essential component of development cycle of large scale applications The main advantage of logging API over plain printf
resides in its ability to disable certain log statements while allowing others to print unhindered. This capability assumes that the logging space, that is, the space of all possible logging statements, is categorized according to some developer-chosen criteria. It provides precise context about a run of the application. Once inserted into the code, the generation of logging output requires no human intervention. Moreover, log output can be saved in a persistent medium to be studied at a later time. In addition to its use in the development cycle, a sufficiently-rich logging package can also be viewed as an auditing tool. Logging does have its drawbacks. It can slow down an application. If too verbose, it can cause scrolling blindness. To alleviate these concerns, log4cplus is designed to be reliable, fast and extensible.
First let me discuss few key concepts of the API. Log4cplus has three main components:
- Layouts
- Appenders
- Loggers
These three types of components work together to enable developers to log messages according to message type and level, and to control at runtime how these messages are formatted and where they are reported.
A layout class determines the format of a log message. You may derive your own classes from Layout, to specify any style of output message that you want. log4cplus comes with three layout classes, SimpleLayout
, TTCCLayout
and PatternLayout
. The following are the examples
SimpleLayout :
DEBUG - Hello world
TTCCLayout :
225 [main] INFO Hello World
PatternLayout:
INFO 21 May 2001 11:00:57,109 HELLO WORLD
An appender class writes the trace message out to some device. log4cplus comes with classes to "append" to standard out, or a named file. The Appender class works closely with the Layout class, and once again you may derive your own appender classes if you wish to log to a socket, a shared memory buffer, or some sort of delayed write device. Maybe most useful appenders built in log4cplus are: FileAppender
, RollingFileAppender
, and ConsoleAppender
. Some of the basic methods in the appender class are listed below.
Appender : public log4cplus::helpers::SharedObject
{
void setLayout(std::auto_ptr<Layout> layout)
void setFilter(log4cplus::spi::FilterPtr f)
void setThreshold(LogLevel th)
void doAppend(const log4cplus::spi::InternalLoggingEvent& event)
}
A logger class does the actual logging. A logger object has two main parts, appenders and log level. Log level controls which messages can be logged by a particular class. When a logger object is created, it starts life with a default appender of standard out and a default priority of none. One or more appenders can be added to the list of destinations for logging. The priority of a logger can be set to NOT_SET_LOG_LEVEL, TRACE_LOG_LEVEL, DEBUG_LOG_LEVEL, INFO_LOG_LEVEL, WARN_LOG_LEVEL, ERROR_LOG_LEVEL, FATAL_LOG_LEVEL
in ascending order of "logginess" or importance level of each message. FATAL
and EMERG
are two names for the same highest level of importance. Each message is logged to a logger object. The logger object has a priority level. The message itself also has a priority level as it wends its way to the log. If the priority of the message is greater than, or equal to, the priority of the logger, then logging takes place, otherwise the message is ignored. The logger priorities are arranged such that : FATAL_LOG_LEVEL > ERROR_LOG_LEVEL > WARN_LOG_LEVEL > INFO_LOG_LEVEL > DEBUG_LOG_LEVEL > TRACE_LOG_LEVEL > NOT_SET_LOG_LEVEL
. NOT_SET_LOG_LEVEL
is the lowest and if a logger object is left with a NOTSET
priority, it will accept and log any message.
Appenders accumulate as a collection on a hierarchy object. When an appender is added to a logger, the default behavior is for the appender to be added to the list of destinations for logging messages. It would be possible to log/trace into two files simultaneously as well as echoing to standard out. But it's possible to set the additivity flag to false, if you do that, adding an appender to a logger will replace the old appender by the new one. By default, loggers do not have a set LogLevel but inherit it from the hierarchy. This is one of the central features of log4cplus. Otherwise, a new instance is created. Some of the basic methods in the Logger class are listed below.
class Logger : public log4cplus::spi::AppenderAttachable
{
static bool exists(const log4cplus::tstring& name)
static LoggerList getCurrentLoggers()
static Hierarchy& getDefaultHierarchy()
static Logger getInstance(const log4cplus::tstring& name)
static Logger getRoot()
including root contained in the default hierachy. Some appenders such as
SocketAppender need to be closed before the application exits. Otherwise,
pending logging events might be lost.
static void shutdown()
void log(LogLevel ll, const log4cplus::tstring& message, const char*
file=NULL, int line=-1)
event without further checks.
void forcedLog(LogLevel ll, const log4cplus::tstring& message, const
char* file=NULL, int line=-1)
circumventing any evaluation of whether to log or not to log the particular log
request.
void callAppenders(const spi::InternalLoggingEvent& event)
void setLogLevel(LogLevel)
log4cplus::tstring getName() const
virtual void addAppender(SharedAppenderPtr newAppender)
virtual SharedAppenderPtrList getAllAppenders()
<CODE>virtual SharedAppenderPtr getAppender(const log4cplus::tstring& name)
virtual void removeAllAppenders()
virtual void removeAppender(SharedAppenderPtr appender)
virtual void removeAppender(const log4cplus::tstring& name)
}
Application framework provides access to a Logger object from every class of the framework. This logger object should be used with the Table 1 macros to create the log events
Severity
| Log4Cplus macro
|
Debug
| LOG4CPLUS_DEBUG
|
Low
| LOG4CPLUS_INFO
|
LOG4CPLUS_WARN
|
LOG4CPLUS_ERROR
|
High
| LOG4CPLUS_FATAL
|
Table 1
Depending on the class where the logging event is sent a different method should be called in order to get the reference of the Logger object. In typical situation
Log4Cplus::Logger& log = getApplicationLogger()
LOG4CPLUS_INFO(log, "here put some information")
NDC
A Nested Diagnostic Context , or NDC in short, is an instrument to distinguish interleaved log output from different sources. Log output is typically interleaved when a server handles multiple clients near-simultaneously. Interleaved log output can still be meaningful if each log entry from different contexts had a distinctive stamp. This is the role of the NDCs.
To build an NDC call the push()
method:
- Contexts can be nested.
- When entering a context, call
NDC.push
. As a side effect, if there is no nested diagnostic context for the current thread, this method will create it. - When leaving a context, call
NDC.pop
. - When exiting a thread make sure to call
NDC.remove()
.
Log4Cplus is available for all major platforms. It can be downloaded from http://sourceforge.net/projects/log4cplus/
Basic Usage
To use log4cplus in your source:
1. First, you have to include files
<pre> #include "log4cplus/logger.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/layout.h"
#include "log4cplus/ndc.h" </pre>
a. If you want a console logger, you have to include these file too:
b. <pre> include "log4cplus/consoleappender.h" </pre>
c. If you want a file logger, you have to include these file too:
d. <pre> #include "log4cplus/fileappender.h" </pre>
2. For a easiest way of use you could write:
<pre>using namespace log4cplus; </pre>
3. Instantiate an appender object. Depending of which logger you want, you have to:
a. Instantiate a console appender:
b. <pre>SharedAppenderPtr myAppender(new ConsoleAppender());</pre>
c. <pre>myAppender->setName("myAppenderName");</pre>
d. Instantiate a file appender:
e. <pre>SharedAppenderPtr myAppender(new FileAppender("myLogFile.log"));</pre>
f. <pre>myAppender->setName("myAppenderName");</pre>
Replace "myLogFile.log"
with the name of log file you like.
4. Instantiate a layout object:
<pre>std::auto_ptr<Layout>; myLayout = std::auto_ptr<Layout>(new log4cplus::TTCCLayout());</pre>
5. Attach the layout object to the appender object:
<pre>myAppender->setLayout( myLayout );</pre>
6. Instantiate the logger attach an appender on it:
<pre>Logger myLogger= Logger::getInstance("myLoggerName");</pre>
7. Attach the appender to the logger:
<pre>myLogger.addAppender(myAppender);</pre>
8. Set the loglevel of the logger to info for example:
<pre>myLogger.setLogLevel ( INFO_LOG_LEVEL );</pre>
The others loglevel are : FATAL_LOG_LEVEL > ERROR_LOG_LEVEL > WARN_LOG_LEVEL > INFO_LOG_LEVEL > DEBUG_LOG_LEVEL > TRACE_LOG_LEVEL > NOT_SET_LOG_LEVEL
.
9. When you want to log a message, use one of these macro depending of the log level the message to log:
<pre>LOG4CPLUS_FATAL(myLogger, "logEvent");
<pre>LOG4CPLUS_ERROR(myLogger, "logEvent");
<pre>LOG4CPLUS_WARN(myLogger, "logEvent") ;
<pre>LOG4CPLUS_INFO(myLogger, "logEvent");
<pre>LOG4CPLUS_DEBUG(myLogger, "logEvent");
10. By defining a LOG4CPLUS_DISABLE_*
, the macros will be disable and won't even be compiled into your executable. *
could be : DEBUG
, INFO
, or WARN
. For example, adding:
<pre>#define LOG4CPLUS_DISABLE_INFO
#define LOG4CPLUS_DISABLE_WARN
#define LOG4CPLUS_DISABLE_DEBUG</pre>
at the begining of your program will disable the DEBUG
, INFO
, and WARN
loglevel.
Conclusions
One of the advantages of the log4cplus is its manageability. Once the log statements have been inserted into the code, they can be controlled with configuration files. They can be selectively enabled or disabled, and sent to different and multiple output targets in user-chosen formats. The log4cplus package is designed so that log statements can remain in shipped code without incurring a heavy performance cost.