How to use Spring BoneCPDataSource bean as data so

2019-05-15 16:07发布

问题:

I would like to log log4j2 messages into relational database.

Documentation for JDBC appender is here. I can use as database connection provider these sources:

  • ConnectionFactory
  • DataSource
  • DriverManager

but is there some way how to use datasource bean (com.jolbox.bonecp.BoneCPDataSource) what we use in entire application?

回答1:

You can do it in 4 steps (step 2 & 3 are log4j2 config for web app):

Step 1: Create log4j2.xml file (without JDBC appender) and put it in WEB-INF folder

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">

    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>

    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>

</Configuration>

Step 2 : Add log4j2-web-2.x.jar to WEB-INF/lib.

Here is the maven repository, you can download the jar from there or copy paste the dependency to your pom.xml if your using maven.

Step 3 : Configure web.xml depending on the servlet api version

See log4j2 documentation for more details. Here is how you configure log4j in a servlet 2.5 web app.

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">

<!-- Logg -->
<listener>
    <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
    <filter-name>log4jServletFilter</filter-name>
    <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>
<!-- /Logg -->

<!-- ... -->
</web-app>

Step 4: Create a spring bean , inject your DataSource bean , and add JDBC Appender configuration dynamically in @PostConstruct method

Here is the code sample ( tested with log4j 2.1 and spring 3.5)

import java.sql.Connection;
import java.sql.SQLException;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig;
import org.apache.logging.log4j.core.appender.db.jdbc.ConnectionSource;
import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class LogUtils{

  @Autowired
  private DataSource dataSource;

  //inner class
  class Connect implements ConnectionSource {
    private DataSource dsource;
    public Connect(DataSource dsource) {
        this.dsource = dsource;
    }
    @Override
    public Connection getConnection() throws SQLException {
        return this.dsource.getConnection();
    }

  }

  public LogUtils() {
    System.out.println("LogUtils");     
  }

  @PostConstruct
  private void init(){
    System.out.println("init LogUtils");    
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();
    ColumnConfig[] cc = {
            ColumnConfig.createColumnConfig(config, "date", null, null, "true", null, null),
            ColumnConfig.createColumnConfig(config, "level", "%level", null, null, null, null),
            ColumnConfig.createColumnConfig(config, "logger", "%logger", null, null, null, null),
            ColumnConfig.createColumnConfig(config, "message", "%message", null, null, null, null),
            ColumnConfig.createColumnConfig(config, "throwable", "%ex{short}", null, null, null, null),
            ColumnConfig.createColumnConfig(config, "salarie_id", "%X{SALARIE_ID}", null, null, null, null)
    } ;     
    Appender appender = JdbcAppender.createAppender("databaseAppender", "true", null, new Connect(dataSource), "0", "sicdb.bo_log", cc);
    appender.start();
    config.addAppender(appender);
    LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
    loggerConfig.addAppender(appender, null, null);
    ctx.updateLoggers();        
  }

  public DataSource getDataSource() {
    return dataSource;
  }

  public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
  }


}

** Limitations : link to doc**

Applications sometimes have the need to customize logging separate from the actual configuration. Log4j allows this although it suffers from a few limitations: 1. If the configuration file is changed the configuration will be reloaded and the manual changes will be lost. 2. Modification to the running configuration requires that all the methods being called (addAppender and addLogger) be synchronized. As such, the recommended approach for customizing a configuration is to extend one of the standard Configuration classes, override the setup method to first do super.setup() and then add the custom Appenders, Filters and LoggerConfigs to the configuration before it is registered for use.