[ 
https://issues.apache.org/jira/browse/LOG4J2-2624?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17269663#comment-17269663
 ] 

Tim Perry commented on LOG4J2-2624:
-----------------------------------

I have the same issue Kamil Hanak describes. To fix it, I think the WebFragment 
logic registers the filter to shut down log4j needs to be separated out from 
the logic that initializes log4j. Using the ServletContainerInitializer to 
start log4j makes sure logging is initialized really early and that is good. 
But it would be nice to make registration of the filter to shut down log4j 
optional so that this could be handled in web.xml.

To help make things concrete, assume the following ServletContextListener is 
registered in a war's web.xml after any log4j configuration.

public class DisplayNameLoggingServletContextListener implements 
ServletContextListener {
    private Logger logger = LoggerFactory.getLogger(getClass());

    public void contextInitialized(ServletContextEvent sce) {
        final String servletContextName = 
sce.getServletContext().getServletContextName();
        logger.info("Loading " + servletContextName + " " + new Date());
    }

    public void contextDestroyed(ServletContextEvent sce) {
        final String servletContextName = 
sce.getServletContext().getServletContextName();
        logger.info("Unloading " + servletContextName + " " + new Date());
    }
}

What I have figured out is that if I use log4j-web's default registration then 
the logging is initialized when the web fragment jar is first processed when 
ServletContainerInitializer.onStartup(...) is called. This is great because it 
is before almost any class in the war file being loaded can request a logger so 
the WebLookup works correctly. However, because the log4j shutdown logic is 
added as a filter in the web fragment, logging is shut down before servlets, 
ServletContextListeners, etc. are shutdown and so log messages get lost during 
shutdown. In particular, the "Unloading <servletContextName> 2021-01-21 13:22" 
message is lost.


However, if I switch the servlet 2.5 method of configuring log4j, then a logger 
can easily be requested before log4j has initialized. In the above example, 
when DisplayNameLoggingServletContextListener is instantiated it is before 
log4j is initialized and WebLookup doesn't return the expected results. In our 
environment, we name the log files off the "display-name" element in the 
web.xml, but it breaks whenever someone creates a logger too soon.

To fix this, I propose breaking apart the logic in 
Log4jServletContainerInitializer so that it can be configured to not register 
the filter to shutdown log4j and instead put the Log4jWebLifeCycle initializer 
into the servlet context. To avoid breaking existing deployments, this is 
controlled by a new parameter IS_LOG4J_AUTO_SHUTDOWN_DISABLED.

final Log4jWebLifeCycle initializer = 
WebLoggerContextUtils.getWebLifeCycle(servletContext);
initializer.start();
initializer.setLoggerContext(); // the application is just now starting to 
start up


if (!"true".equalsIgnoreCase(servletContext.getInitParameter(
        Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED))) {
    servletContext.addListener(new Log4jServletDestroyedListener());
} else {
    servletContext.setAttribute(Log4jWebSupport.LOG4J_INITIALIZER, initializer);
}

Then, we need a way to shut down log4j:
public class Log4jServletDestroyedListener implements ServletContextListener {

    private static final int DEFAULT_STOP_TIMEOUT = 30;
    private static final TimeUnit DEFAULT_STOP_TIMEOUT_TIMEUNIT = 
TimeUnit.SECONDS;

    private static final String KEY_STOP_TIMEOUT = "log4j.stop.timeout";
    private static final String KEY_STOP_TIMEOUT_TIMEUNIT = 
"log4j.stop.timeout.timeunit";

    private static final Logger LOGGER = StatusLogger.getLogger();

    private ServletContext servletContext;
    private Log4jWebLifeCycle initializer;

    public void contextInitialized(final ServletContextEvent event) {
        LOGGER.debug("Log4jServletContextListener ensuring that Log4j started 
up properly.");
        servletContext = event.getServletContext();
        this.initializer = (Log4jWebLifeCycle) 
                servletContext.getAttribute(Log4jWebSupport.LOG4J_INITIALIZER);
        if (initializer == null) {
            LOGGER.warn("Context did not contain required Log4jWebLifeCycle in 
attribute '" 
                    + Log4jWebSupport.LOG4J_INITIALIZER + "'. Please only use "
                    + "Log4jServletDestroyedListener with " 
                    + Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED + " set 
to true");
        }
    }

    public void contextDestroyed(final ServletContextEvent event) {
        if (this.servletContext == null || this.initializer == null) {
            LOGGER.warn("Context destroyed before it was initialized.");
            return;
        }
        LOGGER.debug("Log4jServletDestroyedListener ensuring that Log4j shuts 
down properly.");

        this.initializer.clearLoggerContext(); // the application is finished
        // shutting down now
        if (initializer instanceof LifeCycle2) {
            final String stopTimeoutStr = 
servletContext.getInitParameter(KEY_STOP_TIMEOUT);
            final long stopTimeout = Strings.isEmpty(stopTimeoutStr) ? 
DEFAULT_STOP_TIMEOUT
                    : Long.parseLong(stopTimeoutStr);
            final String timeoutTimeUnitStr = 
servletContext.getInitParameter(KEY_STOP_TIMEOUT_TIMEUNIT);
            final TimeUnit timeoutTimeUnit = 
Strings.isEmpty(timeoutTimeUnitStr) ? DEFAULT_STOP_TIMEOUT_TIMEUNIT
                    : 
TimeUnit.valueOf(timeoutTimeUnitStr.toUpperCase(Locale.ROOT));
            ((LifeCycle2) this.initializer).stop(stopTimeout, timeoutTimeUnit);
        } else {
            this.initializer.stop();
        }
    }
}


Then to use these changes the following is needed in web.xml:
        <context-param>
        <param-name>isLog4jAutoShutdownDisabled</param-name>
        <param-value>true</param-value>
    </context-param>

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


I'd like some feedback from the log4j team: Is this an acceptable fix? If so, 
would you like a pull request?

> ${web:rootDir} not working in weblogic 12c
> ------------------------------------------
>
>                 Key: LOG4J2-2624
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-2624
>             Project: Log4j 2
>          Issue Type: Bug
>    Affects Versions: 2.11.0
>            Reporter: gaurav
>            Priority: Blocker
>
> The web lookup - ${web:rootDir} seems to cause problems on weblogic 12c 
> deployment.
> I suspect that the _*web-lookup plugin is not initialized*_ when weblogic 
> requests the logger at the start of deployment and is unable to create the 
> file.
> What I've done - 
> *1. Added slf4j in weblogic preferred packages of weblogic.xml.*
>     <wls:package-name>org.slf4j.*</wls:package-name>
> *2. log4j-web dependency is present in classpath.*
> *3.web.xml contains following:-* 
> <context-param>
>     <param-name>isLog4jAutoInitializationDisabled</param-name>
>     <param-value>true</param-value>
> </context-param>
> <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>
> *Note - Log4jServletContextListener is placed before all listeners.
> -Have attached stacktrace for your reference.
> [^stacktrace.txt]
> Please assist.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to