How to use Log4j2 in your WildFly applications

Log4j2 is the latest major release of the popular Logging Framework. In this tutorial we will learn how to include Log4j2 configuration file and use it in your deployments running on WildFly.

Overview of Log4j2

Log4j2 is a powerful logging library, developed by Apache, that provides advanced features such as:

  • Asynchronous Logging: Log4j2 allows for logging to occur asynchronously, which can greatly improve the performance of your application. This feature can be especially beneficial for applications that generate a large number of log statements.
  • Filtering: Log4j2 provides the ability to filter log statements based on specific criteria, such as log level, logger name, or message content.
  • Custom Plugins: Log4j2 provides a plugin-oriented architecture, which allows for the easy addition of custom appenders, filters, and other components. This allows for greater flexibility in configuring your logging system.
  • Improved Performance: Log4j2 has has better performance than Log4j 1.x, it uses LMAX Disruptor library which is a high-performance inter-thread messaging library.
  • Improved configuration: Log4j2 allows for configuration of loggers, appenders, and other components through a variety of means, including XML, JSON, and property files. This allows for greater flexibility in configuring your logging system to meet the specific needs of your application.

After this brief overview, let’s see how to configure Log4j2 in WildFly application server.

Configuring Log4j2 in WildFly

Firstly, WildFly allow using the Apache Log4j2 API to send application logging messages using JBoss LogManager implementation. Here is WildFly’s Log4j2 module structure:

.
├── log4j2
│   └── main
│       ├── log4j2-jboss-logmanager-1.1.1.Final.jar
│       └── module.xml
└── main
    ├── jboss-logmanager-2.1.19.Final.jar
    └── module.xml

However, in order to provide a custom Log4j2 configuration to WildFly, we need to exclude the default Logging subsystem from your application. You can do that through a jboss-deployment-structure.xml. In a Web application, you can place it under the WEB-INF folder of your application:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
      <deployment>
         <exclude-subsystems>
             <subsystem name="logging" />
         </exclude-subsystems>
     </deployment>
</jboss-deployment-structure>

On the other hand, if your application is inside an EAR file, you should exclude the subsystem for the sub-deployment. For example:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <sub-deployment name="myapp.war">
       <exclude-subsystems>
           <subsystem name="logging" />
       </exclude-subsystems>
    </sub-deployment>
</jboss-deployment-structure>

With this premise, we will now build a sample Web application which uses a custom Log4j2 configuration file

A Sample Web application to Test Log4j2

Next, we will deploy a sample Web application which prints some messages using Log4j2. For example, let’s add the following Servlet:

@WebServlet(name = "hello", urlPatterns = { "/hello" })
public class HelloWorldServlet extends HttpServlet {


    Logger logger = LogManager.getLogger(HelloWorldServlet.class);

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<h1>Hello World Servlet on WildFly</h1>");
        logger.warn("Hello world Log4j2 on WildFly");
        logger.warn("Request URI {} - Session Id {}.", request.getRequestURI(), request.getSession().getId());

        writer.close();
    }


}

Next, we will add a log4j2.xml configuration file in the resources folder of our Web application.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[Log4j]%d{HH:mm:ss.SSS} %-5level - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

As you can see, this log4j2.xml configuration file contains a custom PatternLayout so that we can check at first sight if our configuration is in place.

Finally, add the dependencies to build the application:

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

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

If using Gradle, the equivalent configuration is:

compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.19.0'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.19.0'

That’s all. You should have a project tree that resembles the following one:

src
└── main
    ├── java
    │   └── com
    │       └── mastertheboss
    │           └── servlet
    │               └── HelloWorldServlet.java
    ├── resources
    │   └── log4j2.xml
    └── webapp
        └── WEB-INF
            ├── beans.xml
            └── jboss-deployment-structure.xml

Next, deploy the application on WildFly and verify that the log output matches with Log4j2 configuration. For example:

jboss wildfly log4j2 tutorial

Source code: You can find the source code for this example application on Github: https://github.com/fmarchioni/mastertheboss/tree/master/log/log4j2

Using an external Log4j2 configuration file

You can instruct WildFly to use a configuration file which is in an external location of your File System. For example, let’s copy the log4j2.xml file into the $JBOSS_HOME/standalone/configuration:

cp log4j2.xml $JBOSS_HOME/standalone/configuration

Then, we will instruct WildFly to pickup log4j2 configuration file in its “standalone/configuration” folder:

 /system-property=log4j.configurationFile:add(value=${env.JBOSS_HOME}/standalone/configuration/log4j2.xml)

Keeping your Log4j2 libs in sync

To simplify your project maintenance, you can include a Bill of Material to keep them in sync your Log4j2 libraries:

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-bom</artifactId>
			<version>2.19.0</version>
			<scope>import</scope>
			<type>pom</type>
		</dependency>
	</dependencies>
</dependencyManagement>

With that in place, you don’t need to specify the version of the single Log4j artifacts:

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

Using a Logging Facade for Log4j2

In many cases, you could prefer using an agnostic approach to write your application logs. For example, you can use the SLF4J API to produce your logs. Then, you can add as Logging Provider Log4j2. This approach is more flexible and allows to change the Logging provider very easily.

To learn more, we recommend checking this article: How to configure SLF4J in WildFly applications

Security Alert (December 2021)

Please note, there is a flaw in some Log4j2 releases (2.0.0 < 2.15) which is categorized as severe security issue.

Read more here: How to handle CVE-2021-44228 in Java applications

log4j:WARN No appenders could be found for logger

The error message “log4j:WARN No appenders could be found for logger” typically occurs when using the Log4j logging framework in a Java application, and Log4j is unable to find an appender to output log messages. To resolve this error, you need to configure Log4j properly by specifying an appender.

To solve this issue make sure you are placing Log4j configuration file in the correct folder so that the application classpath is able to find it.

A possible workaround, if you are not able to place the Log4j configuration file is configure Log4j programmatically. For example:

Logger root = Logger.getRootLogger();
root.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));

Logger.getRootLogger() gets a reference to the root logger in the Log4j hierarchy. The root logger is the top-level logger that all other loggers inherit from. Therefore, any log statements made in your application code will be processed by Log4j Root Logger. Log messages will be then formatted according to the specified pattern layout and then printed to the console.

Conclusion

In summary, configuring Log4j2 in WildFly is an essential task for any developer or administrator working with Java EE applications. With the right configuration and troubleshooting techniques in place, you can gain better control over your application’s logging, making it easier to monitor and troubleshoot issues, ultimately leading to more reliable and robust deployments on the WildFly application server.

Additionally, we addressed the “logger not found” error, which can be a source of frustration when configuring Log4j2 in WildFly. Understanding the hierarchical nature of loggers and the importance of specifying the correct logger name can help you diagnose and resolve this issue effectively.

Found the article helpful? if so please follow us on Socials