SpringBoot整合logback实现日志输出
文章目录
本文介绍了SpringBoot整合logback实现日志输出的配置流程,主要包括:
1、使用logback-spring.xml配置logback
2、使用webFilter拦截WEB请求
3、使用自定义的拦截器拦截其他形式触发的交易
1、增加配置文件
1.1、增加logback-spring.xml配置
在resources目录下,新建一个xml配置文件,内容如下:
- logback-spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false">
<!-- use application.properties -->
<springProperty name="APP_NAME" source="spring.application.name"/>
<property name="log.dir" value="${user.home}/log/" />
<timestamp key="ymd" datePattern="yyyy-MM-dd"/>
<timestamp key="byMillionSecond" datePattern="HH_mm_ss.SSS"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${log.dir}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
<!-- 每个日志文件的大小上线,超过这个上线之后就产生新的文件 -->
<maxFileSize>20MB</maxFileSize>
<totalSizeCap>300GB</totalSizeCap>
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<!-- 控制台 生产去除-->
<appender-ref ref="STDOUT"/>
<!-- 交易及异常日志-->
<appender-ref ref="FILE"/>
</root>
<!-- 增加MyBatis的日志输出功能,如果别的模块有需要打印的,需要copy下面的代码,并将name改为对应dao存放的完整包路径即可 -->
<logger name="com.iambest.druid.dao" level="debug" />
</configuration>
1.2、配置application.properties
在application.properties文件中增加一行配置:
- application.properties
spring.application.name=druid_study
1.3、logback-spring.xml配置讲解
这里我们定义了一个变量,APP_NAME,值取的是application.properties中配置的spring.application.name这个键
<springProperty name="APP_NAME" source="spring.application.name"/>
这里定义了日志的存放目录,使用用户主目录下的log目录,兼容不同的操作系统,支持linux和win以及mac os
<property name="log.dir" value="${user.home}/log/" />
这里定义了控制台输出的格式,其中我们定义了一个自定义的参数traceId,这个后续我们会讲解
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
这里定义了打印到文件中的输出格式,同样的也加入了一个自定义的参数traceId
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${log.dir}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
<!-- 每个日志文件的大小上线,超过这个上线之后就产生新的文件 -->
<maxFileSize>20MB</maxFileSize>
<totalSizeCap>300GB</totalSizeCap>
</rollingPolicy>
<append>true</append>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
这里定义日志的输出级别,以及使用那些配置;注意,控制台的输出需要在生产环境去除掉
<!-- 日志输出级别 -->
<root level="INFO">
<!-- 控制台 生产去除-->
<appender-ref ref="STDOUT"/>
<!-- 交易及异常日志-->
<appender-ref ref="FILE"/>
</root>
这里定义了MyBatis的日志输出配置,需要将我们的dao所在的包定义进去;如果有多个,则需要新建一个logger
<!-- 增加MyBatis的日志输出功能,如果别的模块有需要打印的,需要copy下面的代码,并将name改为对应dao存放的完整包路径即可 -->
<logger name="com.iambest.druid.dao" level="debug" />
2、编写自定义拦截器
在第一节中,我们的配置文件里面增加了一个自定义的参数traceId,我们用来追踪一笔交易,方便我们进行检索;交易的触发有几种方式,我们这里拦截常用的两种方式:一种是web互联网发起的交易,另一种则是系统定时或者异步交易发起的;
对于异步或者定时触发的交易,我们拦截使用了
Action
结尾的交易;当然你也可以自定义其他的方式
,都是可以支持的。
2.1、增加Web拦截器
定义一个Web拦截器,使用MDC.put(),增加自定义的日志参数
2.1.1、编写Filter
编写LogMdcFilter
拦截/*下的请求,对所有的请求都增加一个自定义的参数traceId
- LogMdcFilter
package com.iambest.druid.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.UUID;
/**
* @author zhang_wei
* @version 1.0.0
* @Classname LogMdcFilter
* @Date 2021/2/27 23:31
* @Created by zhang_wei
* @since 1.0.0
*/
@WebFilter(urlPatterns = "/*", filterName = "logMdcFilter")
public class LogMdcFilter implements Filter {
private static final String TRACE_ID = "traceId";
private static final Logger logger = LoggerFactory.getLogger(LogMdcFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("LogMdcFilter...");
insertMdc();
try {
filterChain.doFilter(servletRequest, servletResponse);
}finally {
MDC.remove(TRACE_ID);
}
}
@Override
public void destroy() {
}
/**
* 增加日志的序号
*/
private void insertMdc(){
UUID uuid = UUID.randomUUID();
MDC.put(TRACE_ID, uuid.toString());
}
}
2.1.2、将Filter加到Spring容器中
我们定义了Filter之后,需要将Filter加入到Spring的容器中才能生效;有两种方法:
1、在Filter上增加
@Service
或者@Component
注解2、在Application主类上增加
@ServletComponentScan
注解我这里使用
@ServletComponentScan
注解
- DruidApplication
@ServletComponentScan(basePackages = {"com.iambest.druid.filter"})
@SpringBootApplication
public class DruidApplication {
public static void main(String[] args) {
SpringApplication.run(DruidApplication.class, args);
}
}
2.2、 自定义异步触发交易的拦截器
1、我们需要引入切面的包,在pom.xml中进行引入
2、指定包下所有以
Action
结尾命名的类,都进行拦截并处理
- pom.xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
- LogMdcAspect
package com.iambest.druid.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import java.util.UUID;
/**
*
* 切面日志
*
* @author zhang_wei
* @version 1.0.0
* @Classname LogMdcAspect
* @Date 2021/2/28 9:41
* @Created by zhang_wei
* @since 1.0.0
*/
@Aspect
@Component
public class LogMdcAspect {
private static final String TRACE_ID = "traceId";
@Pointcut("within(com.iambest.druid..*Action*) &&execution(public * *(..))")
public void logPointCut(){
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MDC.put(TRACE_ID, UUID.randomUUID().toString());
Object result = point.proceed();
MDC.remove(TRACE_ID);
return result;
}
}
3、测试
分两部分进行测试
1、测试Web接入的交易
2、测试一个其他方式触发的交易,
3.1、编写Controller
我们编写一个Controller,拦截
getAll
请求,查询数据库中的数据,并通过日志输出结果
- StudInfoController
/**
* @author zhang_wei
* @version 1.0.0
* @Classname StudInfoController
* @Date 2021/2/26 11:07
* @Created by zhang_wei
* @since 1.0.0
*/
@Controller
public class StudInfoController {
Logger logger = LoggerFactory.getLogger(StudInfoController.class);
@Autowired
StudentInfoService studentInfoService;
@RequestMapping("/getAll")
@ResponseBody
public List<StudentInfo> getAllStudentInfos() {
List<StudentInfo> result = studentInfoService.findAll();
logger.info("result={}", result);
return result;
}
}
3.2、编写一个RunnerImpl及Action类
我们这里编写一个
ApplicationRunnerImpl
类,实现ApplicationRunner
接口,实现在容器启动市执行一些代码操作这里我们吊起一个线程类,在这个
TimerRunnerThread
线程类中新启动一个Executor
线程来模拟定时触发交易
- ApplicationRunnerImpl
package com.iambest.druid.runner;
import com.iambest.druid.thread.TimerRunnerThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* @author zhang_wei
* @version 1.0.0
* @Classname ApplicationRunnerImpl
* @Date 2021/3/1 21:04
* @Created by zhang_wei
* @since 1.0.0
*/
@Component
public class ApplicationRunnerImpl implements ApplicationRunner {
@Autowired
TimerRunnerThread thread;
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
thread.start();
}
}
- TimerRunnerThread
package com.iambest.druid.thread;
import com.iambest.druid.action.TimerRunnerAction;
import com.iambest.druid.util.SpringContextUtil;
import org.springframework.stereotype.Service;
/**
* @author zhang_wei
* @version 1.0.0
* @Classname TimerRunnerThread
* @Date 2021/3/5 14:08
* @Created by zhang_wei
* @since 1.0.0
*/
@Service
public class TimerRunnerThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Executor executor = new Executor();
executor.start();
}
class Executor extends Thread {
@Override
public void run() {
int i = 0;
while (i < 5){
TimerRunnerAction action = SpringContextUtil.getBean(TimerRunnerAction.class);
action.execute();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
i++ ;
}
}
}
}
- TimerRunnerAction
package com.iambest.druid.action;
import com.iambest.druid.entity.StudentInfo;
import com.iambest.druid.service.StudentInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author zhang_wei
* @version 1.0.0
* @Classname TimerRunnerAction
* @Date 2021/3/5 14:30
* @Created by zhang_wei
* @since 1.0.0
*/
@Component
public class TimerRunnerAction {
static final Logger logger = LoggerFactory.getLogger(TimerRunnerAction.class);
@Autowired
StudentInfoService studentInfoService;
public void execute() {
logger.info("TimerRunnerAction={}", getClass());
List<StudentInfo> result = studentInfoService.findAll();
logger.info("result={}", result);
}
}
3.3、启动服务进行测试
我们启动服务,查看控制台的输出,如下:
- 验证模拟定时触发的输出
- 验证web入口的日志输出
- 查看文件
在我的用户目录下的log目录,
存在一个按照我们配置生成的文件,查看其中的日志输出,也是正常的,也正确的打印出我们自定义的参数
如下: