SpringBoot日志系统
SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4j,是一个日志的实现。
logback主要包含三个组成部分:Loggers(日志记录器)、Appenders(输出目的在)、Layouts(日志输出格式)
logback 主要分为三个模块,分别是:
- logback-core:提供基础功能,是其他两个模块的基础
- logback-classic : log4j的升级,实现了self4j api
- logback-access:用于与sevlet容器进行集成、提供网络访问日志的功能
logback加载filepath配置文件顺序:
logback.groovy、logback-test.xml、logback.xml
添加依赖
spring-boot-starter中已经存在slf4j依赖,不需要额外添加
配置文件
配置logback.xml文件
<?xml version="1.0" encoding="UTF-8"?><configuration><property name="FILE_ERROR_PATTERN"value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} %file:%line: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/><include resource="org/springframework/boot/logging/logback/defaults.xml"/><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level></filter><encoder><pattern>${CONSOLE_LOG_PATTERN}</pattern><charset>UTF-8</charset></encoder></appender><appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"><!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高, 所以我们使用下面的策略,可以避免输出 Error 的日志--><!-- <filter class="ch.qos.logback.classic.filter.LevelFilter">--><!-- <!–过滤 Error–>--><!-- <level>ERROR</level>--><!-- <!–匹配到就禁止–>--><!-- <onMatch>DENY</onMatch>--><!-- <!–没有匹配到就允许–>--><!-- <onMismatch>ACCEPT</onMismatch>--><!-- </filter>--><!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。--><!--<File>logs/info.spring-boot-demo-logback.log</File>--><!--滚动策略,按照时间滚动 TimeBasedRollingPolicy--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间--><FileNamePattern>spring-boot-demo-logging/logs/spring-boot-demo-logback/info.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern><!--只保留最近90天的日志--><maxHistory>90</maxHistory><!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志--><!--<totalSizeCap>1GB</totalSizeCap>--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><!-- maxFileSize:这是活动文件的大小,默认值是10MB --><maxFileSize>2MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">--><!--<maxFileSize>1KB</maxFileSize>--><!--</triggeringPolicy>--><encoder><pattern>${FILE_LOG_PATTERN}</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder></appender><!-- <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">--><!-- <!–如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter–>--><!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">--><!-- <level>Error</level>--><!-- </filter>--><!-- <!–日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。–>--><!-- <!–<File>logs/error.spring-boot-demo-logback.log</File>–>--><!-- <!–滚动策略,按照时间滚动 TimeBasedRollingPolicy–>--><!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">--><!-- <!–文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间–>--><!-- <FileNamePattern>logs/spring-boot-demo-logback/error.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>--><!-- <!–只保留最近90天的日志–>--><!-- <maxHistory>90</maxHistory>--><!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">--><!-- <!– maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 –>--><!-- <maxFileSize>2MB</maxFileSize>--><!-- </timeBasedFileNamingAndTriggeringPolicy>--><!-- </rollingPolicy>--><!-- <encoder>--><!-- <pattern>${FILE_ERROR_PATTERN}</pattern>--><!-- <charset>UTF-8</charset> <!– 此处设置字符集 –>--><!-- </encoder>--><!-- </appender>--><root level="info"><appender-ref ref="CONSOLE"/><appender-ref ref="FILE_INFO"/><!-- <appender-ref ref="FILE_ERROR"/>--></root></configuration>
添加AOP日志
添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
添加AOP代码
package online.yuluo.springbootdemo.aspectj;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.Map;import java.util.Objects;/*** @author yuluo*/@Component@Aspect@Slf4jpublic class AopLog {private static final String START_TIME = "request-start";@Pointcut("execution(public * online.yuluo.springbootdemo.controller.*Controller.*(..))")public void log() {}@Before("log()")public void beforeLog(JoinPoint joinPoint) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();log.info("<<<<<<<请求URL:{}", request.getRequestURL());log.info("<<<<<<<请求IP:{}", request.getRemoteAddr());log.info("<<<<<<<请求开始:{}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());Map<String, String[]> parameterMap = request.getParameterMap();log.info("<<<<<<<请求参数:{}", JSON.toJSONString(parameterMap));Long start = System.currentTimeMillis();request.setAttribute(START_TIME, start);}@Around("log()")public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {Object result = joinPoint.proceed();log.info("<<<<<<<返回值:{}", JSON.toJSONString(result));return result;}/*** 后置操作*/@AfterReturning("log()")public void afterReturning() {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();Long start = (Long) request.getAttribute(START_TIME);Long end = System.currentTimeMillis();log.info("<<<<<<<请求耗时:{}毫秒", end - start);}}
添加sql执行日志
本次引入了p6spy
添加依赖
<!-- Spring Aop --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.github.gavlyukovskiy</groupId><artifactId>p6spy-spring-boot-starter</artifactId><version>1.6.1</version></dependency>
修改application配置文件
url driver-class-name
spring:datasource:url: jdbc:p6spy:mysql://eurekaserver01.com:2333/demo?useSSL=false&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&useSSL=false # 数据库URLusername: demo # 数据库用户名password: EanD5NhsdCWLs6zs # 数据库密码driver-class-name: com.p6spy.engine.spy.P6SpyDriver
添加spy.properties文件
#mybatis 3.2.1以上使用modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory#mybatis 3.2.1以下使用或者不配置#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory# 自定义日志打印logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger#日志输出到控制台#appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger# 使用日志系统记录 sqlappender=com.p6spy.engine.spy.appender.Slf4JLogger# 设置 p6spy driver 代理deregisterdrivers=true# 取消JDBC URL前缀useprefix=true# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.excludecategories=info,debug,result,commit,resultset# 日期格式dateformat=yyyy-MM-dd HH:mm:ss# 实际驱动可多个driverlist=com.mysql.cj.jdbc.Driver# 是否开启慢SQL记录outagedetection=true# 慢SQL记录标准 2 秒outagedetectioninterval=2
AOP打印详细信息
@Component@Slf4j@Aspectpublic class SqlAspectj {@Pointcut("execution(public * online.yuluo.springbootdemo.mapper.*Mapper.*(..))")public void log() {}@Around("log()")public Object sqlLogAround(ProceedingJoinPoint joinPoint) throws Throwable {String name = "-";String result = "Y";long start = System.currentTimeMillis();try {name = joinPoint.getSignature().toShortString();return joinPoint.proceed();} catch (Throwable throwable) {result = "N";throw throwable;} finally {// 执行方法名称,执行时间,执行是否成功long end = System.currentTimeMillis();log.info("name:[{}];result:[{}];time:[{}ms]", name, result, end - start);}}// 返回值@AfterReturning(pointcut = "log()", returning = "returnValue")public void afterReturning(JoinPoint joinPoint, Object returnValue) {log.info("return value is " + returnValue);}}
