本文介绍:日志输出到文件并根据
LEVEL
级别将日志分类保存到不同文件、通过异步输出日志减少磁盘IO
提高性能
Logback
Logback
是由log4j
创始人设计的另一个开源日志组件,它分为下面下个模块:
logback-core
:其它两个模块的基础模块logback-classic
:它是log4j
的一个改良版本,同时它完整实现了slf4j API
使你可以很方便地更换成其它日志系统如log4j
或JDK14 Logging
logback-access
:访问模块与Servlet
容器集成提供通过Http
来访问日志的功能包括:
TRACE
、DEBUG
、INFO
、WARN
和ERROR
。
TRACE
特别详细的系统运行完成信息,业务代码中,不要使用。(除非有特殊用意,否则请使用DEBUG
级别替代)
DEBUG
debug
信息要有意义,最好有相关参数);DEBUG
信息DEBUG
,需要使用开关进行管理,不能一直开启。INFO
系统运行信息
Service
方法中对于系统/业务状态的变更;外部接口部分
说明
Service
都进行出入口打点记录,单一、简单Service
是没有意义的;OrderAction
操作(业务状态变更);INFO
记录入参;Service
为SOA
架构,那么可以看成是一个外部接口提供方,那么必须记录入参;WARN
不应该出现但是不影响程序、当前请求正常运行的异常情况:
即将接近临界值的时候,例如:缓存池占用达到警告线;
业务异常的记录,比如:当接口抛出业务异常时,应该记录此异常。
ERROR
影响到程序正常运行、当前请求正常运行的异常情况:
SQLException
和除了业务异常之外的所有异常(RuntimeException
和Exception
)。不应该出现的情况:
如果进行了抛出异常操作,请不要记录ERROR
日志,由最终处理方进行处理:
反例(不要这么做):
try{
....
}catch(Exception ex){
String errorMessage=String.format("Error while reading information of user [%s]",userName);
logger.error(errorMessage,ex);
throw new UserServiceException(errorMessage,ex);
}
SpringBoot
中 logback
SpringBoot
工程自带logback
和slf4j
的依赖,所以重点放在编写配置文件上,需要引入什么依赖,日志依赖冲突统统都不需要我们管了;logback
框架会默认加载classpath
下命名为logback-spring
或logback
的配置文件。ERROR
日志和其他日志分开,并且不同级别的日志根据时间段进行记录存储。logback-spring.xml
<configuration>
<springProperty scope="context" name="logPath" source="logging.path"/>
<appender name="CONSOLE-LOG" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%npattern>
layout>
appender>
<appender name="INFO-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>DENYonMatch>
<onMismatch>ACCEPTonMismatch>
filter>
<encoder>
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%npattern>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/info.%d.logfileNamePattern>
<maxHistory>30maxHistory>
rollingPolicy>
appender>
<appender name="ERROR-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERRORlevel>
filter>
<encoder>
<pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%npattern>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/error.%d.logfileNamePattern>
<maxHistory>30maxHistory>
rollingPolicy>
appender>
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0discardingThreshold>
<queueSize>256queueSize>
<appender-ref ref="INFO-LOG"/>
appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0discardingThreshold>
<queueSize>256queueSize>
<appender-ref ref="ERROR-LOG"/>
appender>
<root level="info">
<appender-ref ref="CONSOLE-LOG" />
<appender-ref ref="INFO-LOG" />
<appender-ref ref="ERROR-LOG" />
root>
configuration>
Web
请求做入参/出参打印server:
port: 8088
# 日志输出文件地址
logging:
path: ./springboot-demo-log/logback-demo/logs
WebLogAspect
详见文中注释
@Aspect
@Component
@Slf4j
public class WebLogAspect {
/**
* 进入方法时间戳
*/
private Long startTime;
/**
* 方法结束时间戳(计时)
*/
private Long endTime;
public WebLogAspect() {
}
/**
* 定义请求日志切入点,其切入点表达式有多种匹配方式,这里是指定路径
*/
@Pointcut("execution(public * cn.van.log.logback.web.controller.*.*(..))")
public void webLogPointcut() {
}
/**
* 前置通知:
* 1. 在执行目标方法之前执行,比如请求接口之前的登录验证;
* 2. 在前置通知中设置请求日志信息,如开始时间,请求参数,注解内容等
*
* @param joinPoint
* @throws Throwable
*/
@Before("webLogPointcut()")
public void doBefore(JoinPoint joinPoint) {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//获取请求头中的User-Agent
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
//打印请求的内容
startTime = System.currentTimeMillis();
log.info("请求开始时间:{}" , LocalDateTime.now());
log.info("请求Url : {}" , request.getRequestURL().toString());
log.info("请求方式 : {}" , request.getMethod());
log.info("请求ip : {}" , request.getRemoteAddr());
log.info("请求参数 : {}" , Arrays.toString(joinPoint.getArgs()));
// 系统信息
log.info("浏览器:{}", userAgent.getBrowser().toString());
log.info("浏览器版本:{}", userAgent.getBrowserVersion());
log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
}
/**
* 返回通知:
* 1. 在目标方法正常结束之后执行
* 1. 在返回通知中补充请求日志信息,如返回时间,方法耗时,返回值,并且保存日志信息
*
* @param ret
* @throws Throwable
*/
@AfterReturning(returning = "ret", pointcut = "webLogPointcut()")
public void doAfterReturning(Object ret) throws Throwable {
endTime = System.currentTimeMillis();
log.info("请求结束时间:{}" , LocalDateTime.now());
log.info("请求耗时:{}ms" , (endTime - startTime));
// 处理完请求,返回内容
log.info("请求返回 : {}" , ret);
}
/**
* 异常通知:
* 1. 在目标方法非正常结束,发生异常或者抛出异常时执行
* 1. 在异常通知中设置异常信息,并将其保存
*
* @param throwable
*/
@AfterThrowing(value = "webLogPointcut()", throwing = "throwable")
public void doAfterThrowing(Throwable throwable) {
// 保存异常日志记录
log.error("发生异常时间:{}" , LocalDateTime.now());
log.error("抛出异常:{}" , throwable.getMessage());
}
}
包含一个正常请求接口和异常接口。
@RestController
@RequestMapping("/log")
public class TestController {
/**
* 测试正常请求
* @param msg
* @return
*/
@GetMapping("/normal/{msg}")
public String getMsg(@PathVariable String msg) {
return msg;
}
/**
* 测试抛异常
* @return
*/
@GetMapping("/exception/{msg}")
public String getException(@PathVariable String msg){
// 故意造出一个异常
Integer.parseInt("abc123");
return msg;
}
}
打开浏览器,访问http://localhost:8088/log/normal/hello
日志打印如下:
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [65] [INFO ] 请求开始时间:2019-02-24T22:37:50.892
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [66] [INFO ] 请求Url : http://localhost:8088/log/normal/hello
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [67] [INFO ] 请求方式 : GET
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [68] [INFO ] 请求ip : 0:0:0:0:0:0:0:1
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [70] [INFO ] 请求参数 : [hello]
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [72] [INFO ] 浏览器:CHROME
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [73] [INFO ] 浏览器版本:76.0.3809.100
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [74] [INFO ] 操作系统: MAC_OS_X
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [88] [INFO ] 请求结束时间:2019-02-24T22:37:50.901
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [89] [INFO ] 请求耗时:14
[2019-02-24 22:37:50.050] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-1] [91] [INFO ] 请求返回 : hello
访问:http://localhost:8088/log/exception/hello
日志打印如下:
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [65] [INFO ] 请求开始时间:2019-02-24T22:39:57.728
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [66] [INFO ] 请求Url : http://localhost:8088/log/exception/hello
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [67] [INFO ] 请求方式 : GET
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [68] [INFO ] 请求ip : 0:0:0:0:0:0:0:1
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [70] [INFO ] 请求参数 : [hello]
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [72] [INFO ] 浏览器:CHROME
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [73] [INFO ] 浏览器版本:76.0.3809.100
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [74] [INFO ] 操作系统: MAC_OS_X
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [104] [ERROR] 发生异常时间:2019-02-24T22:39:57.731
[2019-02-24 22:39:57.057] [cn.van.log.aop.logback.WebLogAspect] [http-nio-8088-exec-9] [105] [ERROR] 抛出异常:For input string: "abc123"
[2019-02-24 22:39:57.057] [org.apache.juli.logging.DirectJDKLog] [http-nio-8088-exec-9] [175] [ERROR] Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NumberFormatException: For input string: "abc123"] with root cause
java.lang.NumberFormatException: For input string: "abc123"
Github 示例代码
博主祖传秘籍 Spring Boot 葵花宝典 开源中,欢迎前来吐槽,提供线索,告诉博主接下来更新哪方面文章,共同进步!
最新文章,欢迎关注:公众号-风尘博客;交流观点,欢迎添加:个人微信