1. Introduction

In this tutorial, we’ll learn about creating a custom Log4j2 appender. If you’re looking for the introduction to Log4j2, please take a look at this article.

Log4j2 ships with a lot of built-in appenders which can be used for various purposes such as logging to a file, to a database, to a socket or to a NoSQL database.

However, there could be a need for a custom appender depending on the application demands.

Log4j2 is an upgraded version of Log4j and has significant improvements over Log4j. Hence, we’ll be using the Log4j2 framework to demonstrate the creation of a custom appender.

2. Maven Setup

We will need the log4j-core dependency in our pom.xml to start with:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.19.0</version>
</dependency>

The latest version log4j-core can be found here.

3. Custom Appender

There are two ways by which we can implement our custom appender. First is by implementing the Appender interface and the second is by extending the AbstractAppender class. The second method provides a simple way to implement our own custom appender and that is what we will use.

For this example, we’re going to create a MapAppender. We’ll capture the log events and store them in a Concurrent**HashMap with the timestamp for the key.

Here’s how we create the MapAppender:

@Plugin(
  name = "MapAppender", 
  category = Core.CATEGORY_NAME, 
  elementType = Appender.ELEMENT_TYPE)
public class MapAppender extends AbstractAppender {

    private ConcurrentMap<String, LogEvent> eventMap = new ConcurrentHashMap<>();

    protected MapAppender(String name, Filter filter) {
        super(name, filter, null);
    }

    @PluginFactory
    public static MapAppender createAppender(
      @PluginAttribute("name") String name, 
      @PluginElement("Filter") Filter filter) {
        return new MapAppender(name, filter);
    }

    @Override
    public void append(LogEvent event) {
        eventMap.put(Instant.now().toString(), event);
    }
}

We’ve annotated the class with the @Plugin annotation which indicates that our appender is a plugin.

The name of the plugin signifies the name we would provide in the configuration to use this appender. The category specifies that category under which we place the plugin. The elementType is appender.

We also need a factory method that will create the appender. Our createAppender method serves this purpose and is annotated with the @PluginFactory annotation.

Here, we initialize our appender by calling the protected constructor and we pass the layout as null as we are not going to provide any layout in the config file and we expect the framework to resolve default layout.

Next, we’ve overridden the append method which has the actual logic of handling the LogEvent. In our case, the append method puts the LogEvent into our eventMap. 

4. Configuration

Now that we have our MapAppender in place, we need a lo4j2.xml configuration file to use this appender for our logging.

Here’s how we define the configuration section in our log4j2.xml file:

<Configuration xmlns:xi="http://www.w3.org/2001/XInclude" packages="com.baeldung" status="WARN">

Note that the packages attribute should reference the package that contains your custom appender.

Next, in our appender’s section, we define the appender. Here is how we add our custom appender to the list of appenders in the configuration:

<MapAppender name="MapAppender" />

The last part is to actually use the appender in our Loggers section. For our implementation, we use MapAppender as a root logger and define it in the root section.

Here’s how it’s done:

<Root level="DEBUG">
    <AppenderRef ref="MapAppender" />
</Root>

5. Error Handling

To handle errors while logging the event we can use the error method inherited from AbstractAppender.

For example, if we don’t want to log events which have a log level less than that of WARN.

We can use the error method of AbstractAppender to log an error message. Here’s how it’s done in our class:

public void append(LogEvent event) {
    if (event.getLevel().isLessSpecificThan(Level.WARN)) {
        error("Unable to log less than WARN level.");
        return;
    }
    eventMap.put(Instant.now().toString(), event);
}

Observe how our append method has changed now. We check the event’s level for being greater than WARN and we return early if it is anything less than WARN.

6. Conclusion

In this article, we’ve seen how to implement a custom appender for Log4j2.

While there are many in-built ways of logging our data by using Log4j2’s provided appenders, we also have tools in this framework that enable us to create our own appender as per our application needs.

As usual, the example can be found over on Github.