javalogback

Why, when configuring Logback from XML, DriverManagerConnectionSource is not starting resulting in failed connections?


After upgrading to slf4j 2.0.13, logback 1.5.6 and logback-db 1.2.11.1, the XML configuration for logback that was previously just fine is failing. As near as I can tell, the framework is trying to initialize loggers before the DriverManagerConnectionSource is initialized. My suspicion is that I have missed a new configuration requirement.

I have compared the configuration file to the DBAppender example at https://logback.qos.ch/manual/appenders.html

When I trace through the source, DBAppenderBase.start() contains this line:

DBUtil.getDialectFromCode(connectionSource.getSQLDialectCode());

The connectionSource (an instance of DriverManagerConnectionSource) contains null as the SQLDialectCode. When I delve deeper, the DriverManagerConnectionSource is supposed to set the SQLDialectCode in its start() method, based on the URL provided in the connectionSource.url tag. I have usedd a breakpoint in the debugger to establish that the DriverManagerConnectionSource.start() is never invoked.

I can't be the only person who has ever used the DBAppender, so I am wondering what configuration requirement has changed since we last updated our POM references to logback.

pom.xml:

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.13</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.6</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.5.6</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>1.4.14</version>
        </dependency>
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>7.4</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback.db</groupId>
            <artifactId>logback-classic-db</artifactId>
            <version>1.2.11.1</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback.db</groupId>
            <artifactId>logback-core-db</artifactId>
            <version>1.2.11.1</version>
        </dependency>

Java code:

package com.me.myApp;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@WebListener
public class MyContextListener implements ServletContextListener  {
    
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.setProperty("my.web.container", sce.getServletContext().getServerInfo());
    
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("Shutting down.");
    }

}

XML configuration:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">

    <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
        <resetJUL>true</resetJUL>
    </contextListener>
    
    <property file="/my/properties/my-datastore.properties"/>
    <!-- To enable JMX Management -->
    <jmxConfigurator />
  
    <!-- Notification Appender -->
    <appender name="me.notification.appender" class="ch.qos.logback.classic.db.DBAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
            <driverClass>org.postgresql.Driver</driverClass>
            <url>jdbc:postgresql://me.myapp.com:5432/mydb</url>
            <user>postgres</user>
            <password>postgres</password>
        </connectionSource>
        <encoder>
            <pattern>%-5level %logger{0} - %msg%n</pattern>
            <pattern>Id : %X{Id},Name : %X{Name},ModelName : %X{ModelName} %thread %date{ISO8601} %-5level %logger{5} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="me.notification" level="info">
        <appender-ref ref="me.notification.appender" />
        <appender-ref ref="logFile" />
    </logger>
</configuration>

Logback code that is not doing what I expect based on this XML configuration:

public abstract class DBAppenderBase<E> extends UnsynchronizedAppenderBase<E> {

    protected ConnectionSource connectionSource;
    protected boolean cnxSupportsGetGeneratedKeys = false;
    protected boolean cnxSupportsBatchUpdates = false;
    protected SQLDialect sqlDialect;

    protected abstract Method getGeneratedKeysMethod();

    protected abstract String getInsertSQL();

    @Override
    public void start() {

        if (connectionSource == null) {
            throw new IllegalStateException("DBAppender cannot function without a connection source");
        }

        sqlDialect = DBUtil.getDialectFromCode(connectionSource.getSQLDialectCode());
        if (getGeneratedKeysMethod() != null) {
            cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys();
        } else {
            cnxSupportsGetGeneratedKeys = false;
        }
        cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates();
        if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) {
            throw new IllegalStateException(
                            "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect");
        }
...
        // all nice and dandy on the eastern front
        super.start();
    }

Solution

  • There were several interacting issues which each (individually) resulted in the same symptom (DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method and without a specific SQL dialect), which I was able to trace with the help of the debugger.

    1. The wrong logback.xml file was being referenced. There was a spurious file in the classpath.
    2. When the bad config file was removed, the classloader was not finding the jdbc driver. Maven clean + maven rebuild + Eclipse build, followed by Eclipse Reset Perspective
    3. When the correct config file was used and the classloader was finding the jdbc driver, the jdbc password was incorrect so no connection was obtained.