WildFly 19 and Log4J2


In this post, I would like to share my story about external configuration file of Log4J2. I developed a Java web application in WildFly 19 application server. It was my pet project. I followed few strict rules about 3rd party libraries. For example, I did not use log4j2-web library/jar file. It was the challenge because in some workplace you cannot have luxury to get all bells and whistle. You have to accept the situation.

Therefore, I took this challenge to use external configuration file of Log4J2 in the web application without depending on log4j2-web jar file.

My development environment's logging configuration is not same as production environment. In this case, I do not want to swap between development configuration and production configuration file during deployment.   

The above situation can be mitigate by two ways:

  1. Use external logging configuration file.
  2. User application server specific logging system. For exampke, JBoss/WildFly has logging subsystem.

Personally, I do not want to stick with application server specific logging system. Suppose, if I change my application server vendor, there is good chance I have to change the configuration in the logging system also. Finally, the application server logging system may not support log4J2. For example, Log4J2 API is supported by WildFly 22 only.

Therefore, I have to use external logging configuration file which does not depend on the application server. In addition, I have the freedom to use any logging library.

Step 1:

I have created very simple Log4J2 XML configuration file in this example. I have put the Log4J2 configuration file in the configuration folder of the WildFly 19.

For example:

${WildFly_HOME}/standalone/configuration/log4j.xml

log4j.xml:

  <configuration status="DEBUG">
    <appenders>
      <file filename="${WildFly_HOME}/standalone/log/app.log" name="LogToFile">
        <patternlayout>
          <pattern>%d %p %c{1.} [%t] %m%n</pattern>
        </patternlayout>
      </file>
    </appenders>
    <loggers>
      <!--avoid duplicated logs with additivity=false-->
      <logger additivity="false" level="trace" name="org.mfh114.testApp">
        <appenderref ref="LogToFile"> </appenderref
      ></logger>
      <root level="error"> 
      	<appenderref ref="LogToFile">
      </appenderref></root>
    </loggers>
  </configuration> 

Here, ${WildFly_Home} is the directory of the WildFly application server and the log file "app.log" will be created in the ${WildFly_HOME}/standalone/log/ directory. Therefore, if you follow my example, you have to replace ${WildFly_HOME} with your actual WildFly location.

Step 2:

Now, I do have a question. How do we read the configuration file and when will we read the configuration file?

In the web application, we should not load the static resources for each request; for example, configuration file. It does not make sense. The log configuration file must be loaded once and read during deployment not per request. How do we resolve it?

Since my web application is Java based, I implemented Servlet Context Listener interface. Servlet Context Listener will be triggered only when a web application is deployed or server is started/restarted and a web application is undeployed or server is stopped/restarted.

Here is my  implementation of ServletContextListener:

public class Log4J2ConfigLoader implements ServletContextListener {

	private LoggerContext ctx;

	public Log4J2ConfigLoader() {
	}

	public void contextInitialized(javax.servlet.ServletContextEvent sce) {

		try {
			String log4jConfigFile = System.getProperty("jboss.server.config.dir") 
                		+ File.separator + "log4j2.xml";

			String name = "testApp";
			File logConfigFile = new File(log4jConfigFile);
			URI uri = logConfigFile.toURI();

			this.ctx = Configurator.initialize(name, null, uri);
			this.ctx.start();
                
		} catch (Exception e) {
			throw new Exception(e.getMessage(), e);
		}

	}

	public void contextDestroyed(javax.servlet.ServletContextEvent sce) {
		this.ctx.stop();
	}

}

Using the Configurator, I loaded external configuration file. Configurator returns LoggerContext object. The context name, class loader is null and configuration location are passed to create LoggerContext. It is a heavy object connected with the classloader. Therefore, it should be created once when application's life cycle is started and destroyed when application's life cycle is ended. For this reason, it is created in the ServletContextListener.contextInitialized method. The method will be called by servlet container during deploying the application or starting the server. On the other hand the context is distroyed/shutdown in the ServletContextListener.contextDestroyed method. The method will be called by servlet container during undeploying the application or stopping the server.


Comments

Popular posts from this blog

There is a process already using the admin port 4848 -- it probably is another instance of a GlassFish server; ERROR

How to install Homebrew in Mac OSX (High Sierra)