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();
}
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.