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.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