The new log4j 2.0

By on

Before a while a new major version of the well known log4j logging framework was released. Since the first alpha version appeared 4 more releases happened! You see, there is much more activity than with the predecessor log4j 1. And seriously, despite log4j 2s young age it is ways better. This blog will give an overview on a few of the great features of Apache log4j 2.0.

Modern API

In old days, people wrote things like this:

if (logger.isDebugEnabled()) {
   logger.debug("Hi, " + u.getA() +   + u.getB());
}

Many have complained about it: it is very unreadable. And if one would forget the surrounding if-clause a lot of unnecessary Strings would be the result. These days String creation is most likely optimized by modern the JVM, but should we rely on JVM optimizations?

The log4j 2.0 team thought about things like that and improved the API. Now you can write the same like that:

logger.debug("Hi, {} {}", u.getA(), u.getB());

The new and improved API supports placeholders with variable arguments, as other modern logging frameworks do.

There is more API sweetness, like Markers and flow tracing:

private Logger logger = LogManager.getLogger(MyApp.class.getName());
private static final Marker QUERY_MARKER = MarkerManager.getMarker("SQL");
...

public String doQuery(String table) {
   logger.entry(param);
   logger.debug(QUERY_MARKER, "SELECT * FROM {}", table);
   return logger.exit();
}

Markers let you identify specific log entries quickly. Flow Traces are methods which you can call at the start and the end of a method. In your log file you'll would see a lot of new logging entries in trace level: your program flow is logged.

Example on flow traces:

19:08:07.056 TRACE com.test.TestService 19 retrieveMessage - entry
19:08:07.060 TRACE com.test.TestService 46 getKey - entry

Plugin Architecture

log4j 2.0 supports a plugin architecture. Extending log4j 2 to your own needs has become dead-easy. You can build your extensions the namespace of your choice and just need to tell the framework where to look.

<configuration  packages="de.grobmeier.examples.log4j2.plugins">

With the above configuration log4j 2 would look for plugins in the de.grobmeier.examples.log4j2.plugins package. If you have several namespaces, no problem. It is a comma separated list.

An simple plugin looks like that:

@Plugin(name = "Sandbox", type = "Core", elementType = "appender")
public class SandboxAppender extends AppenderBase {

    private SandboxAppender(String name, Filter filter) {
        super(name, filter, null);
    }

    public void append(LogEvent event) {
        System.out.println(event.getMessage().getFormattedMessage());
    }

    @PluginFactory
    public static SandboxAppender createAppender(
         @PluginAttr("name") String name,
         @PluginElement("filters") Filter filter) {
        return new SandboxAppender(name, filter);
    }
}

The method with the annotation @PluginFactory seves as, well, factory. The two arguments of the factory are directly read from the configuration file. I achieved that behavior with using @PluginAttr and @PluginElement on my arguments.

The rest is pretty trivial too. As I wrote an appender, I have chosen to extend AppenderBase. It forces me to implement the append() method which does the actual job. Besides Appenders, you can even write your own Logger or Filter. Just take a look at the docs.

Powerful Configuration

The new log4j 2 configuration has become easier. Don't worry, if you could understand the old way to configure log4j, it will take you only a short time to learn the differences to the new way. It looks like that:

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
  <appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </appenders>
  <loggers>
    <logger name="com.foo.Bar" level="trace" additivity="false">
      <appender-ref ref="Console"/>
    </logger>
    <root level="error">
      <appender-ref ref="Console"/>
    </root>
  </loggers>
</configuration>

Please look at the appenders section. You are able to use speaking tags, f.e. matching the name of the appender. No more class names. Well, this XML document cannot be validated of course. In case you urgently need XML validation that, you can still use a more strict xml format, which reminds on the old format:

<appenders>
   <appender type="type" name="name">
     <filter type="type" ... />
   </appender>
   ...
</appenders>

But that's not all. You could even reload your configuration automatically:

<?xml version="1.0" encoding="UTF-8"?>
<configuration monitorInterval="30">
...
</configuration>

The monitoring interval is a value in seconds, minimum value is 5. It means, log4j 2 would reconfigure logging in case something has changed in your configuration. If set to zero or left out, no change detection will happen. The best: log4j 2.0 does not lose logging events at the time of reconfiguration, unlike many other frameworks.

But there is even more exciting stuff. If you prefer JSON to XML, you are free to go with a JSON configuration:

{
    "configuration": {
        "appenders": {
            "Console": {
                "name": "STDOUT",
                "PatternLayout": {
                    "pattern": "%m%n"
                }
            }
        },
        "loggers": {
            "logger": {
                "name": "EventLogger",
                "level": "info",
                "additivity": "false",
                "appender-ref": {
                    "ref": "Routing"
                }
            },
            "root": {
                "level": "error",
                "appender-ref": {
                    "ref": "STDOUT"
                }
            }
        }
    }
}

The new configuration is really powerful and supports things like property substitution. Check them more in detail on the manual pages.

log4j 2.0 is a team player: slf4j and friends

Apache log4j 2.0 has many integrations. It works well if your application still runs with Commons Logging. Not only that, you can use it with slf4j. You can bridge your log4j 1.x app to use log4j 2 in the background. And another opportunity: log4j 2 supports Apache Flume. Flume is a distributed, reliable, and available service for efficiently collecting, aggregating, and moving large amounts of log data.

Java 5 Concurrency

From the docs: "log4j 2 takes advantage of Java 5 concurrency support and performs locking at the lowest level possible.". Apache log4j 2.0 does address many deadlock issues which are still in log4j 1.x. If you suffer from log4j 1.x memory leaks, you should definitely look at log4j 2.0.

Built at the Apache Software Foundation

As log4j 1.x, log4j 2.x is an Apache Software Foundation project. It means it is licensed with the "Apache License 2.0" and thus will stay free. You can build your own product upon it, you are free to modify it to your own needs and you can redistribute it, even commercially. You don't need to care on intellectual property. The Apache Software Foundation does care about that. If you would like to know more on licensing and how you can use log4j 2.0 for your own project, I refer you to the Licensing FAQ.

Tags: Apache log4j, Apache Logging, Java, Open Source