It’s been almost six months since we released Log4j 2.5, and we have a bunch of neat things added since then. The biggest change is that now you can operate Log4j in a garbage-free mode during steady state logging. Sounds a bit jargony? The basic idea here is that once your application is initialized and doing its thing (the steady state), logging no longer contributes towards memory pressure, thus you have more control over memory allocation and gargabe collection. Various Java performance gurus point out that logging tends to add a lot of overhead to applications, and one major reason is due to garbage generated by the logging framework. The less garbage objects generated by the framework, the less time your application needs to spend collecting said garbage.

In prior versions of Log4j (and every other major logging framework), various temporary objects are created with each call to Logger.log(). For instance, the method Logger.info(String message, Object... args) would allocate a temporary array to hold the arguments passed to the method. In Log4j 2.6, we’ve added unrolled versions of these methods up to 10 arguments. In contrast, SLF4J 1.7.21 only provides up to two unrolled arguments.

Another source of garbage is the boxing of primitive values into their object forms. For certain primitives and small values, these boxed values are already cached by the JVM and don’t really add any overhead, but in general, you might not want your application to allocate new Long instances just to log user IDs. A helper class has been added called Unbox which can be used to convert a primitive directly into a StringBuilder that can be used (and re-used) by the Logger methods. For example:

import static org.apache.logging.log4j.util.Unbox.box;
// ...
Logger logger = LogManager.getLogger();
// ...
long userId = 1234567890L;
double pi = Math.PI;
logger.debug("User ID: {}; Pi: {}.", box(userId), box(pi));

That particular change is only the tip of the iceberg, but it is something that was added to log4j-api. Do note that if you implement the Logger or ExtendedLogger interfaces manually instead of inheriting from AbstractLogger, you will need to update your code. There were a large number of method additions to these interfaces to accomodate garbage-free logging, so I encourage the use of the abstract base classes. In order to take advantage of the new unrolled methods, you’ll have to recompile your code, but I’m sure you do that all the time anyways. Existing compiled code will still work (we take semantic versioning of log4j-api seriously), but it will still use the varargs versions of methods as expected.

One other important API addition to support garbage-free logging is the addition of Logger.log(CharSequence) style methods. For example, suppose you have code that builds a StringBuilder to format your own log message. Previously, this would be converted to a String which is an unnecessary allocation before it was further used by another StringBuilder later when formatting the log message to a Layout. Now you can log the StringBuilder (or any CharSequence) directly and no intermediary String is created to hold it.

When it comes to log4j-core, most of the commonly used appenders, layouts, filters, lookups, etc., have been also made garbage-free. The primary technique used to make this garbage-free is to re-use objects as much as possible as well as caching these objects in ThreadLocals usually. However, this has major implications to traditional web applications due to the use of thread pools and the subsequent memory leaks caused by naively using ThreadLocal in a web app. Because of this, the use of ThreadLocals is disabled by default when deployed in a servlet container. This can be overridden via the system propery log4j2.is.webapp with the value false. Note that if you enable this in a web app, you cannot undeploy or redeploy your web app without introducing memory leaks. Instead, you’ll have to take down your whole servlet container and bring it back up. When using projects like Spring Boot, this is a perfectly good scenario to override the safe defaults. Another alternative is to deploy Log4j directly to your server’s global libraries so that it isn’t part of any WAR deployments (similar to how you should be using JDBC drivers).

Another API addition is the LogManager.shutdown() method to manually terminate Log4j. To support this in a custom Log4j provider, your LoggerContext implementation must also implement Terminable (only needed if your LoggerContext needs to have a terminate() method).

One last thing of note is that we’ve improved the properties file syntax and support quite a bit in this release. This includes simplifications to the syntax as well as some bugfixes that only affected the properties format. There has been some experimental work done towards supporting log4j 1.x style properties files, but that particular feature hasn’t made it to a release yet. Despite the XML, JSON, and YAML formats being objectively better configuration file formats than the properties format, we’ll continue to improve properties support over time.

One last thing about configuration files: you can now merge multiple config files together to form a composite configuration!