Introduction to the Logger Module
The Logger module (aka Log4q) adopts its primary design from the well known log4j library, therefore it implements the following primary base classes:
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::getLevelInfo());
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::getLevelInfo());
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);
}
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
v0.1
- the initial version of the Logger module