basic introduction to log4cplus
Log4cplus has three main components:
- Layouts
- Appenders
- Loggers
These three type of components found the basic of the log4cplus framework.
here is a simplified uml graph illustrating the relationships of those classes:
(1) A "Layout" class determines the format of the log message(how do we print out the message).
and log4cplus comes with three layout class:
- SimpleLayout
- TTCCLayout
- PatternLayout
These classes accept log event and emits formatted text of messages, they work closely with appender.
(2)An appender writes log message to somewhere(file,socket,etc).
Appender functions like a callback.
when log message is appended to a logger, the appender associated with that logger will be called.
appender is the "seam" for user to intecept the log message.
User can provide their own appenders to catch log message, and this features is extremely useful when we need to keep an eye on the log message.
log4cplus provides interface to help us do that.
as simple as it is, we only need to inherit from a base class : log4cplus::Appender.
and implement several virtual function.
class myAppender:public log4cplus::Appender { protected: virtual void close(); virtual void append(const log4cplus::spi::InternalLoggingEvent& event); }
function close() will be called when the associated logger is shutdown.
appender should clean up its relative resource here.
function append() is the callback that will be called when a log message is set to the logger( the logger that myAppender is associated).
the parameter InternalLoggingEvent contains all the necessary information about an incoming "log message".
so ,all you have to do is to get the message from the event ,and formatted the message by Layout class or by your own way.
Appender must have an associated Layout class and an unique name. they have default values. however you can specify them by calling.
Appender->setName("name") Appender->setLayout(layout);
ok, so far I have discussed how to creat an appender, next i will show you how to put it in the right position to work.
The key is : appender attached to at least one Logger.
logger keeps a list of appenders belonging to it.
if you want to intercept message of some logger, just insert your appender to its appender list.
MyAppender app;
Logger myLoger;
myLoger.addAppender(app);
(3)a logger does the actually logging.
logger has two main parts: Appender , log level.
log level is for controlling which log message can be logged.
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
those levels are 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.
Logging is done by logger.
Logger mylogger = log4cplus::Logger::getInstance("my logger"); mylogger.log(level,"message");
logger is organized in hierarchy: logger belongs to a specific hierarchy.
every hierarchy has root logger.
logger has a parent(default is root logger).
so all the loggers are linked in a tree-like structure.
logger is not supposed to be created by user.
log4cplus uses a factory pattern to create logger.
Logger::getInstance (const log4cplus::tstring& name, spi::LoggerFactory& factory)
the code above help retrieve a logger with name "name". If the named
logger already exists, then the existing instance will be returned.
Otherwise, a new instance is created.