Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @PreDestroy: No logging because Logback stops too soon

In my project, I am using Logback as logging facility. I have the following class

@Component
class Test {
    @PreDestroy
    public void destroy() {
        try {
            ...
        } catch (Exception e) {
            LoggerFactory.getLogger(getClass()).error(e.getLocalizedMessage(), e);
        }
    }
}

Now, I undeploy the servlet. When an exception occurs, Logback is not printing the message and stack trace. This is because Logback is cleaning up before the destroy() is called by Spring. When undeploying the servlet, this is the first (and last) log line:

15:46:19,084 |-INFO in ch.qos.logback.classic.servlet.LogbackServletContextListener@7957fe56 - About to stop ch.qos.logback.classic.LoggerContext [default]

I verified that Logback stops first by adding a System.out.println("..."); in the destroy().

Is there any way to fix this?

My dependencies:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>5.0.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>99-empty</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jcl</artifactId>
    </dependency>
</dependencies>

Please note that spring-jcl is used to route commons-logging to slf4j (which will route to logback). I am not using jcl-over-slf4j.

like image 878
Harrold_dev Avatar asked Nov 08 '17 15:11

Harrold_dev


People also ask

Does spring use Logback instead of Log4j?

Spring Boot supports Log4j 2 for logging configuration if it is on the classpath. If you use the starters for assembling dependencies, you have to exclude Logback and then include log4j 2 instead. If you do not use the starters, you need to provide (at least) spring-jcl in addition to Log4j 2.

Why does Spring Boot use Logback?

When using starters, Logback is used for logging by default. Spring Boot preconfigures it with patterns and ANSI colors to make the standard output more readable. As we can see, the default logging level of the Logger is preset to INFO, meaning that TRACE and DEBUG messages are not visible.

Which logging framework is best for Spring Boot?

If you are using Spring Boot Starters, Logback will provide a good support for logging. Besides, Logback also provides a use of good support for Common Logging, Util Logging, Log4J, and SLF4J.

Can you control logging with Spring Boot How?

Spring Boot uses Commons Logging for all internal logging but leaves the underlying log implementation open. Default configurations are provided for Java Util Logging, Log4J2, and Logback. In each case, loggers are pre-configured to use console output with optional file output also available.


2 Answers

There is a documented way to disable to disable the registering of the LogbackServletContextListener:

You may disable the automatic the installation of LogbackServletContextListener by setting a named logbackDisableServletContainerInitializer in your web-application's web.xml file. Here is the relevant snippet.

<web-app>
    <context-param>
        <param-name>logbackDisableServletContainerInitializer</param-name>
        <param-value>true</param-value>
    </context-param>
    .... 
</web-app>

Note that logbackDisableServletContainerInitializer variable can also be set as a Java system property an OS environment variable. The most local setting has priority, i.e. web-app first, system property second and OS environment last.

I would imagine you'll probably want to write your own shutdown hook if this is the case and stop the LoggerContext

like image 125
Dovmo Avatar answered Oct 17 '22 18:10

Dovmo


To pick up Dovmo's answer. It's all about ordering. I have the same issue on Tomcat 8.5.x. Bacially, the initializer does an addListener, you don't have any control over the listener. At shutdown, Tomcat reverse-iterates that list, picking the Logback listener first: Spring won't log anything.

A custom listener won't help because you cannot prepend it. What works for me on Tomcat is:

<listener>
    <listener-class>ch.qos.logback.classic.servlet.LogbackServletContextListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <param-name>logbackDisableServletContainerInitializer</param-name>
    <param-value>true</param-value>
</context-param>
like image 45
Michael-O Avatar answered Oct 17 '22 17:10

Michael-O