log4j2 JDBC manager cannot connect to the database

2019-06-20 09:02发布

问题:

I'm trying to configure log4j v2 with mysql, but it returns this error:

2014-08-01 15:35:24,819 ERROR Unable to write to database [jdbcManager{ description=databaseAppender, bufferSize=0, connectionSource=factory{ public static java.sql.Connection it.prisma.presentationlayer.webui.ConnectionFactory.getDatabaseConnection() }, tableName=logs, columns=[ { name=message, layout=%message, literal=null, timestamp=false } ] }] for appender [databaseAppender]. org.apache.logging.log4j.core.appender.AppenderLoggingException: Cannot write logging event or flush buffer; JDBC manager cannot connect to the database.

Caused by: java.sql.SQLException: Failed to obtain connection from factory method.

Caused by: java.sql.SQLException: No suitable driver found for jdbc:mysql://10.41.1.68:3306/test

My configuration is similar to doc, with the only difference of this:

new PoolableConnectionFactory(connectionFactory, pool, null, "SELECT 1", false, false, Connection.TRANSACTION_READ_COMMITTED);

I think that my tomcat7 is well configured because I can log with log4j v1.

回答1:

Ok the solution is:

1- create a Connection Factory class

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;


public class ConnectionFactory {

    private static interface Singleton {
        final ConnectionFactory INSTANCE = new ConnectionFactory();
    }

    private final DataSource dataSource;

    private ConnectionFactory() {

        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.exit(0);
        }

        Properties properties = new Properties();
        properties.setProperty("user", "root");
        properties.setProperty("password", "root");

        GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<PoolableConnection>();
        DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
                "jdbc:mysql://example.org:3306/exampleDb", properties
        );
        new PoolableConnectionFactory(connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED);
        this.dataSource = new PoolingDataSource(pool);
    }

    public static Connection getDatabaseConnection() throws SQLException {
        return Singleton.INSTANCE.dataSource.getConnection();
    }
}

2- create a configuration file into src/main/resources called log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE" monitorInterval="30">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
        <JDBC name="databaseAppender" tableName="logs">
            <ConnectionFactory class="my.pack.ConnectionFactory"
                method="getDatabaseConnection" />
            <!-- <Column name="id" literal="LOGGING.APPLICATION_LOG_SEQUENCE.NEXTVAL" /> -->
            <Column name="date" isEventTimestamp="true" />
            <Column name="level" pattern="%level" />
            <Column name="message" pattern="%message" />
            <Column name="class" pattern="%class" /> 
        </JDBC>
    </Appenders>
    <Loggers>
        <Root level="trace">
            <AppenderRef ref="Console" />
            <AppenderRef ref="databaseAppender" />
        </Root>
    </Loggers>
</Configuration>

3- create a table like this:

4- add mysql connector to {tomcat}/bin

Maven hints:

    <commons-dbcp.version>1.4</commons-dbcp.version>

    <commons-pool.version>1.6</commons-pool.version>

    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>${commons-dbcp.version}</version>
    </dependency>

    <dependency>
        <groupId>commons-pool</groupId>
        <artifactId>commons-pool</artifactId>
        <version>${commons-pool.version}</version>
    </dependency>


回答2:

For future readers. I got this error when I was using hikari. My code (that is BAD and does not work)....is below.

So I changed to Mateo's answer (here at this question, the answer he posted), and I've back in business.

I'm leaving this answer, in case someone else hits the issue.

PS My logging dependencies were "clean". see https://www.logicbig.com/tutorials/spring-framework/spring-boot/log4j2.html

[INFO] +- org.springframework.boot:spring-boot-starter-log4j2:jar:2.1.3.RELEASE:compile
[INFO] |  +- org.apache.logging.log4j:log4j-slf4j-impl:jar:2.11.2:compile
[INFO] |  |  \- org.apache.logging.log4j:log4j-api:jar:2.11.2:compile
[INFO] |  +- org.apache.logging.log4j:log4j-core:jar:2.11.2:compile
[INFO] |  +- org.apache.logging.log4j:log4j-jul:jar:2.11.2:compile
[INFO] |  \- org.slf4j:jul-to-slf4j:jar:1.7.25:compile

..

    <HikariCP.version>3.1.0</HikariCP.version>

..

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import javax.sql.DataSource;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class LogDataSource {

    private static LogDataSource logDataSource = new LogDataSource();

    public static final String ENVIRONMENT = "environment";
    private Properties prop;
    public String environment;
    private HikariDataSource hikariDataSource;

    private LogDataSource() {
        try {
            environment = System.getProperty(ENVIRONMENT);
            String url = getProperty("LogDatabaseConnectionString");
            HikariConfig hikariConfig = new HikariConfig();
            hikariConfig.setPoolName("eisltier2LogPool");
            hikariConfig.setConnectionTestQuery("SELECT 1");
            hikariConfig.setDataSourceClassName(getProperty("dataSourceClassName"));

            String dsClassName = hikariConfig.getDataSourceClassName();
            try {
                Class.forName(dsClassName);
            } catch (ClassNotFoundException e) {
                throw new NullPointerException(String.format("DataSourceClassName did not load. (DataSourceClassName='%s')", dsClassName));
            }

            hikariConfig.setMaximumPoolSize(Integer.valueOf(getProperty("hikari.maximumPoolSize")));
            hikariConfig.setIdleTimeout(Integer.valueOf(getProperty("hikari.idleTimeout")));
            Properties dsProperties = new Properties();
            dsProperties.put("url", url);
            hikariConfig.setDataSourceProperties(dsProperties);
            HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
            this.hikariDataSource = hikariDataSource;

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static DataSource getConnection() {
        DataSource returnItem = logDataSource.hikariDataSource;
        return returnItem;
    }

    public String getProperty(String key) throws IOException {

        if (prop == null) {
            prop = new Properties();
            try(InputStream inputConnectionStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("connection.properties");){
            prop.load(inputConnectionStream);
            }catch (Exception e) {
                throw new IOException(e);
            }
        }
        String value = ((environment == null || environment == "") ? prop.getProperty(key)
                : prop.getProperty(environment + "." + key));
        return value;
    }
}