Separating Log4J application logging from the server.log in JBoss EAP

Solution Verified - Updated -

Environment

  • Red Hat JBoss Enterprise Application Platform (EAP)
    • 6
    • 7
  • Log4j / slf4j / custom logging API deployed with application.
  • log4j.xml or logj.propertiesconfiguration
  • Per application loggging files

Issue

  • Application logging going to server.log when log4j.xml is packaged in our application.
  • Messages are not logged in server.log.
  • I would like to separate application logging configuration file outside the JBoss configuration
  • My logging isn't working correctly, and I'm seeing messages like:

    16:17:03,188 INFO  [stdout] (Finalizer) log4j: Finalizing appender named [myAppender1].
    16:17:03,189 INFO  [stdout] (Finalizer) log4j: Finalizing appender named [myAppender2].
    
  • logging-profile not working in domain mode

  • Is it possible to create the separate log file for each application where app1.war and app2.war contains same package name ?

    app1.war --> app1.log
    app2.war --> app2.log 
    
  • No logging from EJBs and Servlets packaged in the EAR . We've tried putting the following jboss-deployment-structure.xml within the EJB's META-INF folder with no success:

  • Exclude JBoss Logging Subsystem for Deployments.
  • How do we configure logging to use our logging APIs.

Resolution

There are several steps involved in this, and it's important that each step is followed correctly, or this will not work. The high level steps are::

  • Package a copy of log4j.jar in your deployment
  • Add the system property -Dorg.jboss.as.logging.per-deployment=false 'Ref'
  • Create a jboss-deployment-structure.xml to exclude org.apache.log4j from your deployment and all subdeployments.
  • Explicitly initialize your Log4J configuration using a PropertyConfigurator or DOMConfigurator in your application.

Package log4j.jar in your application along with your Log4J configuration file (log4j.xml / log4j.properties) in the classpath (which would be in the root of a jar or in /WEB-INF/classes for a war).

Remember, "all subdeployments" means that if you have an EAR that there should be a jboss-deployment-structure.xml at the EAR level, and then another one inside of each WAR or any other type of nested deployment you have inside of that EAR.

Add the system property -Dorg.jboss.as.logging.per-deployment=false, which disables the deployer which scans for the logging configuration files in your deployment. This will prevent JBoss from trying to create a log context for your deployment.

If your logging configuration file has a different name than the standard ones (e.g. log4j.xml, log4j.properties), you will then need to initialize your logging framework in your code e.g. a ServletContextInitialzer if you're using a WAR:

import java.io.IOException;
import java.net.URL;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
import org.apache.log4j.xml.DOMConfigurator;

@javax.servlet.annotation.WebListener
public class TestListener implements ServletContextListener {
  public void contextInitialized(ServletContextEvent event) {
    URL log4jXml = Thread.currentThread().getContextClassLoader().getResource("/my-log4j.xml");
    if(log4jXml == null)
      throw new IllegalStateException("my-log4j.xml not found");
    DOMConfigurator.configure(log4jXml);
  }
  public void contextDestroyed(ServletContextEvent event) {
  }
}

You will also need to declare an exclusion (as shown below) in order to not use the logging frameworks already provided by JBoss. Create a jboss-deployment-structure.xml in the root of your top level deployment in WEB-INF for wars or META-INF for all other types. Here is an example of what you'd put in the META-INF folder of an EAR:

<jboss-deployment-structure>
  <deployment>
    <exclusions>
      <module name="org.apache.log4j" />
    </exclusions>
  </deployment>
  <sub-deployment name="myapp.war">
    <exclusions>
      <module name="org.apache.log4j" />
    </exclusions>
  </sub-deployment>
</jboss-deployment-structure>

The same file would be put in the WEB-INF folder of a WAR without the <sub-deployment> element. Note that if you have multiple WARs embedded in an EAR, you must configure <sub-deployment/> for each WAR. You can not use a wildcard "*" for it. See Knowledge article #179153

A working example is attached as separate-logfile-example.zip. Extract the archive and follow the instructions described in README.txt. This sample application is based on ejb-in-ear quickstart project.

To create a separate log file for each server instance running in domain mode we can use jboss.node.name in log4j.xml/log4j.properties file, and while starting server instance use -Djboss.node.name=Node_Name property.

log4j.appender.myFileAppender.File=/valid/path/${jboss.node.name}.log 

<param name="File" value="${jboss.server.log.dir}/${jboss.node.name}.log"/>

Root Cause

By default JBoss will look in deployments at deployment time for one of the following logging configuration files: log4.properties,log4j.xml, jboss-log4j.xml, jboss-logging.properties or logging.properties. If JBoss finds one, then it will create a log context for the deployment using the packaged configuration file. This means that if you were using Log4J as your logging framework before and want to use your appenders, then it will "just work", and you can have your application log to its own file.

Diagnostic Steps

  • If you see these messages:

    16:17:03,188 INFO  [stdout] (Finalizer) log4j: Finalizing appender named [myAppender1].
    16:17:03,189 INFO  [stdout] (Finalizer) log4j: Finalizing appender named [myAppender2].
    

    This means that you are using the Log4J implementation from JBoss, not from your deployment. Make sure you closely followed the instructions regarding the exclusion of the org.apache.log4j module from your deployment and all subdeployments.

    To know for sure, you can try executing the following code:

    System.out.println(Logger.class.getPackage().getImplementationTitle());
    System.out.println(Logger.class.getPackage().getImplementationVendor());
    System.out.println(Logger.class.getPackage().getImplementationVersion());
    

    This should output something like this if you're using Red Hat's implementation:

    [stdout] log4j-jboss-logmanager
    [stdout] JBoss by Red Hat
    [stdout] 1.0.2.Final-redhat-1
    
  • You do not require separate "jboss-deployment-structure.xml" for each individual war/jar files packaged inside a ear, instead consolidate all of them into a single "jboss-deployment-structure.xml" within the EAR/META-INF folder as explained above .

Attachments

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.

Comments