Introduction to the Logger Module
The Logger module (aka Log4q) adopts its primary design from the well known log4j library
Other classes can be found in the logger module for performance reasons, such as:
Abstract base classes must be subclassed to implement the desired functionality.
Scenarios
- One Thread
The user code will log to a Logger object, and the logging is performed to the appender synchronously.
Example:
Logger l("mylogger", LoggerLevel::LevelInfo);
LoggerAppenderFile laf("myappender", new LoggerLayoutPattern(), "/var/run/log/mylog.log");
l.addAppender(laf);
laf.open();
....
l.info("hello %s #%d", "world", 1);
l.error("the %s is not perfect", "world");
- Multiple Threads
The user code will log from multiple threads; the events are pushed to a LoggerAppenderQueue object. The processing is done in a dedicated thread when the events are passed to appenders. The user code logging command is non-blocking as it terminates immediately when the event is pushed in the queue.
Example:
our Logger l("mylogger", LoggerLevel::LevelInfo);
sub run() {
while (!done) {
...
l.info("hello %s #%d", "world", 1);
l.error("the %s is not perfect", "world");
...
}
}
LoggerAppenderFile laf("myappender", new LoggerLayoutPattern(), "/var/run/log/mylog.log");
laf.setQueue(new LoggerAppenderQueue());
l.addAppender(laf);
laf.open();
for (int i=0; i<10; i++) {
background run();
}
while (True) {
laf.getQueue().process();
}
- Application Server Running Logging From a Few Sandboxed Program Containers
In this example, the appserver provides a logger API for a few sandboxed programs. The appserver is responsible for the Log4q configuration; i.e. it prepares loggers, appenders, filters, etc. according to configuration and provides the Logger instance to the Program container running the sandboxed code. The sandboxed code will log to this instance; the logging events are processed by the appserver in a dedicated thread which gets the event from a queue and passes it to appenders. Multiple loggers may be configured in a parent/child hierarchy so that a higher logging level (i.e. more event levels) are logged with the logger assigned to the sandbox and fewer (ex: only critical errors) to the global appserver logger.
Example:
LoggerAppenderQueue laq();
LoggerRoot lr("ERROR");
LoggerAppenderFile lar("", new LoggerLayoutPattern(), "/var/run/log/myappserver.log");
lar.setQueue(laq);
lar.open();
lr.addAppender(lar);
foreach string pn in ( .... ) {
Logger l(pn);
LoggerAppenderFile la(pn, new LoggerLayoutPattern(), "/var/run/log/"+pn+".log");
la.setQueue(laq);
la.open();
l.setParent(lr);
l.setAdditivity(True);
l.addAppender(la);
l.setLevel("DEBUG");
Program p(PO_NEW_STYLE);
p.loadModule("Logger");
p.parse('
our Logger logger;
int sub main(string pn) {
logger.log("INFO", "hello %s #%d", "world", 1);
...
return 0;
}
', pn, WARN_DEFAULT);
p.setGlobalVarValue("logger", l);
...
background p.callFunction("main", pn);
}
while (True) {
laq.process(-1);
}
- Application Server Running Many Sandboxed Program Containers
This example is basically the same as the previous example, but to avoid I/O bottlenecks in logging, the appserver processing thread gets the event from a queue and passes it to the appender in another worker thread by a submitting the logging action to a ThreadPool. So events targeted to a particular thread may by processed in different threads but nevertheless serially.
Example:
ThreadPool tp();
LoggerAppenderQueueThreadPool laq(tp, 5);
LoggerRoot lr("ERROR");
LoggerAppenderFile lar("", new LoggerLayoutPattern(), "/var/run/log/myappserver.log");
lar.setQueue(laq);
lar.open();
lr.addAppender(lar);
code processing() = sub () {
while (True) {
laq.process(-1);
}
}
background processing();
foreach string pn in ( .... ) {
Logger l(pn);
LoggerAppenderFile la(pn, new LoggerLayoutPattern(), "/var/run/log/"+pn+".log");
la.setQueue(laq);
la.open();
l.setParent(lr);
l.setAdditivity(True);
l.addAppender(la);
l.setLevel("DEBUG");
Program p(PO_NEW_STYLE);
p.loadModule("Logger");
p.parse('
our Logger logger;
int sub main(string pn) {
logger.log("INFO", "hello %s #%d", "world", 1);
...
return 0;
}
', pn, WARN_DEFAULT);
p.setGlobalVarValue("logger", l);
...
background p.callFunction("main", pn);
}
v1.0
v0.6
- Fixed a bug where file rotate could result in logging exceptions; removed explicit atomic lock operations and implemented low-level atomic file handling instead (issue 4842)
v0.5
v0.4
v0.3
- fixed a race condition handling log file rotation with active logs (issue 4583)
v0.2
- added support for the
%h
and %P
patterns for hostname and PID, respectively (issue 4179)
- allow file appenders to be reopened (issue 4171)
- enable serialization for LoggerEvent objects as well as for them to be submitted directly to
Logger
objects (issue 4164)
v0.1.1
- added Logger::Logger::logArgs() "Logger::logArgs()" (issue 3492)
v0.1
- the initial version of the Logger module