目录结构
Maven依赖
maven配置文件pom.xml如下↓
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>blvyoucan</groupId>
<artifactId>springboot-log4j2-h2database-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--去掉默认日志框架logback↓-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--增加log4j2依赖↓-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--增加h2database依赖↓-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
<!--增加HikariCP连接池依赖↓-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.4</version>
</dependency>
<!--spring jdbc↓-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>com.Application</start-class>
<java.version>1.8</java.version>
<hibernate.version>5.2.1.Final</hibernate.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
H2数据源配置
h2数据源配置类↓
package com.config;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class H2DataSrouceConfiguration {
@Bean(name = "h2Template")
public JdbcTemplate h2Template(){
return new JdbcTemplate(h2DataSource());
}
@Bean(name = "h2DataSource")
//用于识别application.yaml中的db.h2前辍↓
@ConfigurationProperties("db.h2")
public DataSource h2DataSource(){
//采用h2的内存(mem)数据库模式,并设置了数据库初始化脚本h2_init.sql↓
return DataSourceBuilder.create()
.url("jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:/h2_init.sql'")
.driverClassName("org.h2.Driver")
.username("testdb")
.password("testdb").build();
}
}
application.yaml片断,使用配置文件指定h2数据库使用Hikari连接池,可以灵活更换连接池↓
...
db:
h2:
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 5
...
h2_init.sql,创建MEMO_LOGS表↓
--日志表,主键自增
CREATE TABLE IF NOT EXISTS MEMO_LOGS (
EVENT_ID INT AUTO_INCREMENT(1,1) PRIMARY KEY,
EVENT_DATE DATETIME,
LEVEL VARCHAR(20),
LOGGER VARCHAR(255),
MESSAGE VARCHAR(255),
THROWABLE CLOB,
EXT_FIELD VARCHAR(255)
);
log4j2配置
log4j2的ConnectionDataSource类↓
package com.config;
import org.apache.logging.log4j.core.appender.db.jdbc.ConnectionSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Component
public class LogDBConnectionSource implements ConnectionSource{
@Autowired
@Qualifier("h2DataSource")
private DataSource h2DataSource;
@Override
public Connection getConnection() throws SQLException {
return h2DataSource.getConnection();
}
@Override
public String toString() {
return h2DataSource.toString();
}
}
代码实现log4j2的JdbcAppender配置↓
package com.config;
import com.utils.LogMarker;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
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.JdbcAppender;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.filter.MarkerFilter;
import org.slf4j.MarkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class LogDBConfiguration {
@Autowired
private LogDBConnectionSource logDBConnectionSource;
@PostConstruct
public void doConfigure(){
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final org.apache.logging.log4j.core.config.Configuration config = ctx.getConfiguration();
//定义表MEMO_LOGS各字段写入的内容↓
ColumnConfig[] columnConfigs = new ColumnConfig[6];
columnConfigs[0] = ColumnConfig.createColumnConfig(config,"EVENT_DATE",null,null,"true",null,null);
columnConfigs[1] = ColumnConfig.createColumnConfig(config,"LEVEL","%level",null,"false",null,null);
columnConfigs[2] = ColumnConfig.createColumnConfig(config,"LOGGER","%logger",null,"false",null,null);
columnConfigs[3] = ColumnConfig.createColumnConfig(config,"MESSAGE","%message",null,"false",null,null);
columnConfigs[4] = ColumnConfig.createColumnConfig(config,"THROWABLE","%ex{full}",null,"false",null,"true");
columnConfigs[5] = ColumnConfig.createColumnConfig(config,"EXT_FIELD","%X{ext_field}",null,"false",null,null);
//log4j2的Filter过滤"DB_LOG"↓
MarkerFilter markerFilter = MarkerFilter.createFilter("DB_LOG", Filter.Result.ACCEPT, Filter.Result.DENY);
//用于sl4j的Marker同样过滤"DB_LOG"↓
LogMarker.DB_MARKER = MarkerFactory.getMarker("DB_LOG");
//使用JdbcAppender连接数据库,"MEMO_LOGS"是h2_init.sql中创建的日志表↓
final Appender jdbcAppender = JdbcAppender.createAppender("jdbcAppender","true",markerFilter,logDBConnectionSource,null,"MEMO_LOGS",columnConfigs);
jdbcAppender.start();
//将JdbcAppender附加到RootLogger中,也可新建另外的logger↓
LoggerConfig loggerConfig = config.getRootLogger();
loggerConfig.addAppender(jdbcAppender, null, null);
ctx.updateLoggers();
}
}
log4j2-spring.xml配置文件↓
<?xml version="1.0" encoding="UTF-8"?>
<!-- status=debug 可以查看log4j2的装配过程↓ -->
<Configuration status="off">
<Properties>
<Property name="LOG_PATH">logs</Property>
<Property name="LOG_NAME">test</Property>
</Properties>
<Appenders>
<!--日志输出到控制台↓-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{[%t] %-5level %logger{36}} - %msg%n"/>
<!--防止DB_LOG标记的日志输出到控制台-->
<MarkerFilter marker="DB_LOG" onMatch="DENY" onMismatch="ACCEPT"/>
</Console>
<!--日志输出到文件↓-->
<RollingRandomAccessFile name="ProductionLog"
fileName="${LOG_PATH}/${LOG_NAME}.log" filePattern="${LOG_PATH}/${LOG_NAME}.%d{yyyy-MM-dd}.log">
<PatternLayout
pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1"
modulate="true" />
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<!--root的日志输出级别↓-->
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="ProductionLog" />
</Root>
</Loggers>
</Configuration>
LogMarker辅助类↓
package com.utils;
import org.slf4j.Marker;
public class LogMarker {
public static Marker DB_MARKER;
}
使用和验证
controller类
package com.controllers;
import com.utils.LogMarker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/test")
public class TestContollers {
private static final Logger logger = LoggerFactory.getLogger(TestContollers.class);
@Autowired
@Qualifier("h2Template")
private JdbcTemplate h2Template;
@RequestMapping(path="/list")
public List<Map<String,Object>> list(){
try{
throw new Exception("异常测试");
}catch (Exception e){
//将额外字段写入ThreadContext↓
MDC.put("ext_field","额外字段");
//将日志写入数据库↓
logger.error(LogMarker.DB_MARKER,"测试",e);
// MDC.clear();
// logger.error(LogMarker.DB_MARKER,"测试",e);
}
//从数据库中读取日志↓
String sql = "SELECT EVENT_ID,EVENT_DATE,LEVEL,EXT_FIELD,LOGGER,MESSAGE,THROWABLE FROM MEMO_LOGS";
return h2Template.queryForList(sql);
}
}
启动类Application
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
@SpringBootApplication
@EnableAutoConfiguration(exclude = {HibernateJpaAutoConfiguration.class})
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class);
}
}
完整的application.yml
---
server:
port: 8080
db:
h2:
type: com.zaxxer.hikari.HikariDataSource
maximum-pool-size: 5
#默认的spring boot datasource 配置
#spring:
# datasource:
# type: com.zaxxer.hikari.HikariDataSource
# hikari:
# maximum-pool-size: 5
运行启动类Application
浏览器访问http://localhost:8080/test/list
[{"EVENT_ID":1,"EVENT_DATE":1516178866892,"LEVEL":"ERROR","EXT_FIELD":"额外字段","LOGGER":"com.controllers.TestContollers","MESSAGE":"测试","THROWABLE":"java.lang.Exception: 异常测试\r\n\tat com.controllers.TestContollers.list(TestContollers.java:29)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.lang.reflect.Method.invoke(Method.java:498)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:635)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n"}]
能够查出数据,说明配置成功
代码下载