Logback XML配置中DriverManagerConnectionSource未启动致连接失败的原因
问题描述
将依赖升级至slf4j 2.0.13、logback 1.5.6及logback-db 1.2.11.1后,原本正常的Logback XML配置出现故障。经排查,框架似乎在DriverManagerConnectionSource初始化前就尝试初始化日志器,DriverManagerConnectionSource的start()方法未被调用,导致其SQLDialectCode为null(该属性本应通过connectionSource.url标签中的URL在start()方法中设置)。对比官方DBAppender示例后,怀疑遗漏了新版本的配置要求。
相关依赖配置(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代码
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."); } }
Logback XML配置
<?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源码片段
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(); }
解决方案
1. 统一Logback组件版本
当前依赖中logback-access版本为1.4.14,与logback-classic/logback-core的1.5.6版本不一致,版本不兼容可能导致初始化顺序异常。将logback-access升级至1.5.6:
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> <version>1.5.6</version> </dependency>
2. 显式指定SQL方言
在DriverManagerConnectionSource中添加sqlDialectCode配置,避免框架无法自动识别:
<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> <!-- 显式指定PostgreSQL方言 --> <sqlDialectCode>POSTGRESQL</sqlDialectCode> </connectionSource>
3. 调整日志初始化时机
ServletContextListener中提前初始化Logger,可能触发Logback过早初始化。可将Logger声明延迟到方法内部:
@WebListener public class MyContextListener implements ServletContextListener { // 延迟初始化Logger private Logger logger; @Override public void contextInitialized(ServletContextEvent sce) { System.setProperty("my.web.container", sce.getServletContext().getServerInfo()); } @Override public void contextDestroyed(ServletContextEvent sce) { if (logger == null) { logger = LoggerFactory.getLogger(this.getClass()); } logger.info("Shutting down."); } }
4. 优化配置扫描时机
若配置scan="true"导致初始化过程中重新加载配置,可添加scanDelay延迟扫描启动:
<configuration scan="true" scanPeriod="30 seconds" scanDelay="30000">
内容的提问来源于stack exchange,提问作者pojo-guy
相关产品推荐
相关产品推荐

