Smart Tech for a better Web

Customizing Log4j 2.0


We are getting closer and closer to a Log4j 2 GA release and for that reason we should start looking into what it will give us.

Personally I have a lot of reasons to be excited about Log4j 2.0. The community is great, the Apache License gives me a lot of freedom and finally it is protected by the Apache Software Foundation. And hey, its a great piece of software.

In the past weeks I gave a lot of talks around Log4j 2.0 and realized a couple of people love they can extend the framework pretty easily. In times of the Cloud you may want to use a commercial service to collect logs - or even use your own service. Today we need to be more flexible where to push our logs and this is what Log4j 2.0 provides.

With Log4j 1 and Logback you customized by extending classes and implementing interfaces. This alone was do-able but it was not so easy to provide your own configuration elements.

The Log4j manual brings it to the point:

Log4j 1.x allowed for extension by requiring class attributes on most of the configuration declarations. In the case of some elements, notably the PatternLayout, the only way to add new pattern converters was to extend the PatternLayout class and add them via code.

With Log4j 2.0 it is possible to write these customizations:

  • Core: you can write an Appender, Logger or Filter.
  • Converter: you can transform some LogEvent messages (in example: transform a date)
  • Lookups: lookup some resource (in example: get some value from the system properties)
  • Key Providers: you can provide encryption features to your logs

In many cases the Appender is the most interesting thing to deal with. An Appender is the final element of Log4js architecture which actually does something with the Log-Event. In example an Appender might write to a file or sends the Log event to a database.

Let us look at a trivial Appender which really exists: the File Appender.

If we would need to build something like this, we would first annotate our class with @Plugin:

@Plugin(name = "File",
   category = "Core",
   elementType = "appender",
   printObject = true)
public final class FileAppender
   extends AbstractOutputStreamAppender<FileManager> {

Most important the annotation would take the plugins name, its category "Core" and the element type "appender", telling log4j that this is going to be an appender. The Plugins name is being used for our configuration file later.

At this point you are already almost free to implement whatever you want. You should just make sure you implement the Appender-interface. In the FileAppender the AbstractOutputStreamAppender is extended which implements the Appender interface. You don't need to do that actually but it makes sense. Unlike other frameworks Log4j 2.0 operates with byte arrays under the hood which lets you do a lot more things. You often want to keep up that tradition.

There is only one more thing you need to do for your Appender: implement a Plugin-Factory in the Appenders class. A shortened version of the File-Appender follows:

public static FileAppender createAppender(
  @PluginAttribute("fileName") final String fileName,
  @PluginElement("Layout") Layout<? extends Serializable> layout,
  @PluginElement("Filters") final Filter filter
  ...) {

    // Do things...

   return new FileAppender(
     layout, filter, fileName, ...);

The factory method is annotated with @PluginFactory. It's static and creates an instance of the Appender. The factory also deals with the configuration. It accepts attributes which are annotated with @PluginAttribute or @PluginElement. You maybe guess it, those are elements from your configuration. It could look like this:

    <File name="File" fileName="target/mylog.log">
        <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>

@PluginAttribute names attributes from your appender element (in this case "File"). @PluginElement names nested elements, like the PatternLayout being a Layout instance.

Sidenote: you may have noticed the improved readability of the configuration file. This is also a new feature of Log4j 2.0. However if you have some reasons to stick with the strict style, you can still write your configuration in that vein:

<Appender type="File" name="File" fileName="target/mylog.log">

Now assume we got a brand-new plugin which lives in our companies package. How do we make Log4j 2.0 recognize it? It was not trivial with Log4j 1, but its straightforward with the new Log4j.

Simply add one or more packages containing your appenders to the configuration:

<Configuration packages="de.grobmeier.appenders">

The packages will be scanned, and if Plugins are found they are added. If you are concerned on your start time performance, you can even preload your plugins at build time.

Let's recap. It will take three steps to create your own Log4j 2.0 Appender:

  • Create a class which implement Appender and has the @Plugin annotation
  • Create a static method with the @PluginFactory annotation
  • Add your appender to your configuration

With the plugin architecture even complicated setups are possible. In example, you can create an abstract "NoSQL Appender" for NoSQL databases in general and then provide various implementations based on this. This is already reality, as Log4j supports MongoDB and CouchDB.

If you would like to learn more about this advanced usage I suggest you look into the NoSQL package.This link is gone.

Have fun extending and don't forget we would love to see what you did build at the Log4j mailing list.

Tags: #Logging #Log4j 2.0 #Customization #Plugins