How to configure SLF4J in WildFly applications

In this tutorial, we will discuss how to use Simple Logging Facade for Java (SLF4J) with Wildfly application server. SLF4J is a logging facade that provides a unified interface for various logging frameworks, such as Log4j2, java.util.logging, and Logback. It allows for the decoupling of the application code from the underlying logging framework, making it easier to change the logging framework without modifying the application code.

WildFly and SLF4J in a nutshell

Firstly, if you are new to Simple Logging Facade for Java (SLF4J), we recommend checking this tutorial which explains in detail how it works: Getting Started with Simple Logging Facade (SLF4J).

The main difference, compared with a standard Java application, is that the SLF4J API included in WildFly has a classloading configuration designed to use the JBoss logging subsystem. To override this behavior, you need to perform the following steps:

Firstly, exclude the default WildFly logging implementation by adding the following exclusion in a jboss-deployment-structure.xml file. Place this file in the WEB-INF folder if you are building a Web application:

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

Next, include a dependency for your Logging provider. The current stable release of SLF4J is the 2.0.x series (2.0.18 at the time of writing), and it introduced a breaking change on the provider side: implementations built for SLF4J 1.x are no longer binary compatible, so each backend now ships a dedicated SLF4J 2 binding. If you want to wrap Log4j2 with SLF4J, make sure you pull in log4j-slf4j2-impl rather than the older log4j-slf4j-impl artifact, which only targets SLF4J 1.x:

<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-slf4j2-impl</artifactId>
   <version>2.24.1</version>
</dependency>

If your logging backend of choice is Logback instead, you don't need any bridge at all: since version 1.3/1.4 Logback implements the SLF4J 2 API natively, so simply adding logback-classic to your dependencies is enough.

Finally, include the Log configuration file for your specific implementation. In our example, add the file log4j2.xml in your project’s classpath (for example, in the resources folder of the project):

<?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>

That’s all. We will now add a simple Servlet to test that logs are written according to the Logging provider implementation.

A sample Servlet using the SLF4J API

To test our configuration, add a simple Servlet which uses org.slf4j libraries to print some log messages:

package com.sample;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.inject.Inject;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// Import the SLF4J API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
@SuppressWarnings("serial")
@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
  private static final Logger logger = LoggerFactory.getLogger(HelloWorldServlet.class);
 


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		// Log a debug message
		logger.debug("This is a debug message");

		// Log an info message
		logger.info("This is an info message");

		// Log a warning message
		logger.warn("This is a warning message");

		// Log an error message
		logger.error("This is an error message");

        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.println("Hello World");
        writer.close();
    }

}

Next, deploy the Servlet to WildFly and invoke the /HelloWorld URL. You should be able to see in the Server Logs the PatternLayout from our configuration:

Simple Logging Facade for Java (SLF4J)  with WildFly

Source code for this example project: https://github.com/fmarchioni/mastertheboss/tree/master/log/slf4j-wildfly

What's new since SLF4J 2.0: the Fluent Logging API

If you last touched this setup a couple of years ago, it's worth knowing that SLF4J 2.x also added a Fluent API alongside the traditional method calls. It reads more naturally when you need to attach exceptions, key/value pairs or markers to a log statement, and it defers message construction until it's actually needed, which is friendlier on performance than manual parameter arrays:

logger.atInfo()
      .setMessage("User {} logged in from {}")
      .addArgument(username)
      .addArgument(remoteAddr)
      .log();

logger.atError()
      .setCause(exception)
      .addKeyValue("orderId", orderId)
      .log("Order processing failed");

SLF4J 2.x also brought native support for lambda expressions in log guards, so you can lazily compute expensive messages without an explicit isDebugEnabled() check:

logger.atDebug().log(() -> "Expensive payload: " + buildDebugPayload());

These additions are fully backward compatible with the classic logger.info(...)/logger.debug(...) style used above, so you can adopt them incrementally without rewriting existing code.

A note on containerized and OpenShift deployments

If you are running your WildFly application in a container on Kubernetes or OpenShift, plain text console output is usually not the end of the story: log aggregators such as Fluentd/Fluent Bit or Vector typically expect structured, machine-parsable records. Both Log4j2 and Logback support JSON layouts (JsonTemplateLayout for Log4j2, logstash-logback-encoder for Logback) that you can plug into the same appender configuration shown above, so your application logs remain correlated with request IDs and pod metadata once they reach your log backend (e.g. Loki, Elasticsearch, or OpenShift's built-in Logging Operator).

Conclusion

In conclusion, the Simple Logging Facade for Java (SLF4J) is a powerful tool for managing log statements in your WildFly application. It provides a clear and consistent interface for logging, allowing you to easily configure and manage log statements throughout your application. By using SLF4J with WildFly, and taking advantage of the newer Fluent API where it helps, you can improve the readability and maintainability of your code, while also gaining greater control over your application’s logging behavior.


Recommended Articles

Mastering Simple Logging Facade (SLF4J) in Java: A Comprehensive Guide

Learn how SLF4J simplifies Java logging and enhances flexibility. #Java #Logging #Middleware

Fixing the 'SLF4J: Failed to load class' Error in Java Applications

Learn how to resolve this common error with SLF4J by adding the necessary dependencies and choosing a logging implementation.

Mastering Log4j2 Configuration and Usage with WildFly - A Comprehensive Tutorial

Learn how to configure and utilize Log4j2 in your WildFly deployments. Explore advanced features like Asynchronous Logging, Filtering, and Custom Plugins.

How to Configure Log4j2 with JBoss/WildFly and Avoid ClassLoader Issues

Learn how to configure Log4j2 in WildFly applications, avoiding common errors like ClassCastException.