You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 12:13:11