1

I'm working on writing a class that works as a wrapper for a Logback Logger. Specifically it is to facilitate Audit logging. It should be an independent library that people should be able to import and use. The problem I've having has to do with the minimal amounts of documentation there is for declaring LogBack programatically. I am basing the general declaration on another stack overflow question (Setting Logback Appender path programmatically).

The current definition I have is below. Currently I am having trouble getting the Marker filter work to. It seems like the marker filter, as I've declared it, doesn't do anything. Ultimately I want to make sure that no other information aside from Audit logging makes it into the Audit Log files. That is why I have it Deny on mismatch. From my testing it appears that it is just ignored.

Any information or direction in regards to programmatically created Logback files would be much appreciated. Also is there anything I should worry about when a user uses my class to do audit logging then makes a Logback.xml to do their own separate logging? Thank you

import ch.qos.logback.classic.Level;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.filter.EvaluatorFilter;
import ch.qos.logback.classic.boolex.OnMarkerEvaluator;


public class AuditLogger {
    private static Logger logbackLogger;
    final static Marker AUDIT = MarkerFactory.getMarker("AUDIT");

    static {
       LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();

       RollingFileAppender rfAppender = new RollingFileAppender();
       rfAppender.setContext(loggerContext);
       rfAppender.setFile("auditLogFile.currentDay.log");
       TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy();
       rollingPolicy.setContext(loggerContext);
       // rolling policies need to know their parent
       // it's one of the rare cases, where a sub-component knows about its parent
       rollingPolicy.setParent(rfAppender);
       rollingPolicy.setFileNamePattern("auditLogFile.%d{yyyy-MM-dd}.log");
       rollingPolicy.start();

       PatternLayoutEncoder encoder = new PatternLayoutEncoder();
       encoder.setContext(loggerContext);
       encoder.setPattern("[%d{ISO8601}] %5marker - %msg%n");
       encoder.start();

       rfAppender.setEncoder(encoder);
       rfAppender.setRollingPolicy(rollingPolicy);
       rfAppender.start();

       EvaluatorFilter evalFilter= new EvaluatorFilter();
       OnMarkerEvaluator markerEval= new OnMarkerEvaluator();
       markerEval.addMarker("AUDIT");

       evalFilter.setEvaluator(markerEval);
       evalFilter.setOnMatch(FilterReply.ACCEPT);
       evalFilter.setOnMismatch(FilterReply.DENY);

       rfAppender.addFilter(evalFilter);

       logbackLogger = loggerContext.getLogger("AUDIT_LOGGER");
       logbackLogger.addAppender(rfAppender);

       logbackLogger.setLevel(Level.DEBUG);

   }
   public AuditLogger(){}

   public void log(String msg){
       logbackLogger.debug(AUDIT, msg);
   }
}
Community
  • 1
  • 1
JDP10101
  • 81
  • 9
  • So I figured out that I needed to "start()" my EvaluatorFilter and OnMarkerEvaluator. After I put those two statements ("evalFilter.start()" and "markerEval.start()") the filter works. I am still looking for more information on how having both a programmatically-created logback configuration and default xml will interact. – JDP10101 Jun 10 '14 at 18:20

1 Answers1

0

I never found a whole lot of information on programmatically created Logback files but I'll share what I did find from my testing and fiddling around.

First of all in order to adapt Ceki's response on the linked post I used a combination of the logback.xml examples and the online API. For example in order to find out how to do a marker filter I noticed that the xml has a link to the class "ch.qos.logback.core.filter.EvaluatorFilter" and "ch.qos.logback.classic.boolex.OnMarkerEvaluator". I used the API's found here: http://logback.qos.ch/apidocs/ch/qos/logback/core/filter/EvaluatorFilter.html. Remember (as per my comment above) to run the start() method for every filter, appender, encoder, rolling policy, evaluator and anything else you create when initializing a logback instance programmatically. If you don't it will not work.

As for my testing with different combinations of programmatically created and xml created logback instances I got interesting results. It seems like the instance of logback created programmatically and the one created by xml work together. For example if I call logger.debug("msg") from the programmatically created instance the "msg" will also be received and evaluated by the xml created instance. I believe this is because they all share the same "root" logger.

In order for the library to work correctly when the user is not using logback and not output to the console I use the following bit of code. It checks to see if the default logger is the only one that exists and removes the console appender if so.

    if (loggerContext.getLoggerList().size() == 1) {
        Logger root= loggerContext.getLogger("ROOT");
        root.detachAppender("console");
    }

The only fatal error I found was when the user program used log4j and imported a class called "slf4j-log4j12". The program complained about multiple instances of slf4j and died. By removing the "slf4j-log4j12" maven library from the project it worked fine.

JDP10101
  • 81
  • 9