Log4j2 web look up cannot access servletcontext attribute

2.2k Views Asked by At

We are using Tomcat 7.0.54.

The web.xml:

<context-param>
    <param-name>log4jContextName</param-name>
    <param-value>SabaLog4jContext</param-value>
</context-param>

There is sample servlet which starts on load

    <servlet>
        <servlet-name>startUp</servlet-name>
        <servlet-class>foo.StartupServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

The StartupServlet simple as:

public class StartupServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        getServletContext().setAttribute("test", "ATest");
        super.init();
    }

}

The log4j2 can not access the test attribute with ${web:attr.test} and I got the warning as:

INFO: org.apache.logging.log4j.web.WebLookup unable to resolve key 'test'

It seems that Log4j2 works fine but the problem is that it starts before my Startup. I tried to use a servletContextListener class but no luck.

I also tried to disable Log4jAutoInitialization in web.xml and manually start set them as below.

 <listener>
    <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>

<filter>
   <filter-name>log4jServletFilter</filter-name>
   <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>log4jServletFilter</filter-name>
   <url-pattern>/*</url-pattern>
   <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
  <dispatcher>ERROR</dispatcher>
</filter-mapping>

But no luck:(

The log4j2.xml is as below:

<property name="baseFolder">${web:rootDir}/../logs/${web:test}</property>

So how can setup my web.xml so that my code execute before Log4j context.

The web.xml also contains spring Listeners as:

  <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
1

There are 1 best solutions below

1
On BEST ANSWER

First, make sure Tomcat is configured to provide the functionality of a servlet 3.0 container in your web.xml file:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

You'll want the 3.0 functionality so you can specify the order in which the servlets are loaded. Then you'll want to have your own ServletContainerInitializer to initialize the ServletContext attributes. Here's an snippet of one of mine:

/* Initializer that is configured, via web.xml, to initialize before Log4j2's initializer.  This gives us the
 * opportunity to set some servlet context attributes that Log4j2 will use when it eventually initializes.
 */

public class BitColdHardCashContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(final Set<Class<?>> classes, final ServletContext servletContext) throws ServletException {
        if (servletContext.getMajorVersion() > 2) {
            servletContext.log("BitColdHardCashContainerInitializer starting up in Servlet 3.0+ environment.");

        }

        // Set the webapp.name attribute so that Log4j2 may use it to create a path for log files.
        servletContext.setAttribute("webapp.name", servletContext.getContextPath().replaceAll("/", "").trim());

Next, you want your ServerContainerInitializer to run before Log4j2's. In your web.xml, give your servlet a name:

<absolute-ordering>
    <name>BitColdHardCash</name>
    <others/>
</absolute-ordering>

This needs to be be specified before the <servlet> element.

Create a web-fragment.xml file:

<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                                  http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
              version="3.0" metadata-complete="true">
    <name>BitColdHardCash</name>
    <distributable />
    <ordering>
        <before>
            <others />
        </before>
    </ordering>
</web-fragment>

This tells Tomcat to initialize your ServletContainerInitializer first, before anything else, including Log4j2's. This goes in the META-INF directory.

That should do it. One more thing to check would be your catalina.properties file. You are using a version of Tomcat that fixes a bug regarding the calling of ServletContextInitializers. I'm not sure if the bug was in Tomcat source code or in the default supplied catalina.properties file. In the event you are using a catalina.properties file that pre-dates the fix, just crack it open an ensure that log4j*.jar is not included in the list of files specified for the tomcat.util.scan.DefaultJarScanner.jarsToSkip property.