Spring Boot与Logback的运用(自定义异常+AOP)
在开发以及调试过程中,程序员对日志的需求是非常大的,出了什么问题,都要通过日志去进行排查,但是如果日志不清或者杂乱无章,则不利于维护
这边就比较详细的列举几种类型的日志,供大家参考
首先明白logback日志是Spring Boot自带的,不需要引入额外的包
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
点进pom里的核心依赖,就能看见上面几个,是由Spring Boot自动依赖配置好的,我们只要直接使用就好了
比较简单的是直接在application的配置文件里 写参数配置就行了,他提供了日志级别,日志输出路径等,也能满足基本的日志输出
我们这通过xml文件进行配置 logback-spring.xml
这样就能直接引用到xml了,但是为什么能引用到了
就是在logback里有个默认的机制,内部会有几种标准的文件格式,在LogbackLoggingSystem里标注了
@Override
protected String[] getStandardConfigLocations() {
return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy",
"logback.xml" };
}
所以最为标准的为这里面的四种文件格式,但是如果项目中没有,他还提供了扩展文件格式 就是在后面拼上-spring,例如logback.xml 扩展为logback-spring.xml
ok
下面看下xml里面的内容:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 可以在LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="logs" /> <!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- Console 输出设置 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender> <!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/category-server-log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter> </appender> <!-- 出错日志 appender -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<!-- log.dir 在maven profile里配置 -->
<FileNamePattern>${LOG_HOME}/category-server-error-log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender> <!-- 自己打印的日志文件,用于记录重要日志信息 -->
<appender name="MY_INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/category-server-myinfo-log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>15</MaxHistory>
<!--日志文件最大的大小-->
<MaxFileSize>10MB</MaxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="my_info" additivity="true">
<appender-ref ref="MY_INFO_FILE"/>
</logger>
<!--myibatis log configure-->
<logger name="com.example.demo" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</configuration>
这里一共有四块内容,第一是console的日志输出,第二是系统运行日志,第三是警告以上的日志输出(基本上是程序出错日志),第四种是自定义日志
每一块日志由一个appender标签引入
CONSOLE是控制台日志输出,只要规定个格式就行了
FILE是系统运行日志,系统的所有运行信息都会保留,正常我们会把这部分信息保存在硬盘日志文件中,按天按文件大小保存,因为这个内容实在是比较多
ERROR_FILE是WARN级别以上的日志,这块是开发人员和运维人员最多关注的,因为基本上所有的bug都会在这个里面体现
MY_INFO_FILE是自定义日志,想定义自己的日志文件,记录一些重要的信息
这里的日志都是以文件的形式保存在本地,当然像WARN级别以上日志可以异步保存到数据库
日志文件定义好后,接下来就要开始定义业务逻辑了
在针对一些异常日志,我们想尽可能完整准确的抛出异常,一眼就能知道是什么问题,这里我们就需要自定义异常,最多的就是像空指针,数组越界等常见异常
定义基础异常类BaseException继承他的父类RuntimeException
public class BaseException extends RuntimeException { private static final long serialVersionUID = 1L; public BaseException() {
super();
// TODO Auto-generated constructor stub
} public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
} public BaseException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
} public BaseException(String message) {
super(message);
// TODO Auto-generated constructor stub
} public BaseException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
} }
然后全局异常处理类:GlobalExceptionHandler
@CrossOrigin
@RestControllerAdvice
public class GlobalExceptionHandler{ private static Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class); private static final String APPLICATION_JSON = "application/json";
private static final String UTF_8 = "UTF-8"; /**
* BaseException 处理类
* @Title: HandleBaseException
* @Description: TODO
* @param @param e
* @param @return
* @return ResponseMsg
* @throws
*/
@ExceptionHandler(BaseException.class)
@ResponseBody
public ResponseMsg HandleBaseException(RuntimeException e){
//只能输出捕获到的异常,未捕获到的异常不输出到日志,或者通过aop拦截器拦截所有方法
LOGGER.error(getExceptionDetail(e));
//返回失败信息
Route route = new Route();
ResponseMsg responseMsg = new ResponseMsg(route,ReturnMsgEnum.INTERNAL_ERROR.getCode(),
ReturnMsgEnum.INTERNAL_ERROR.getMsg(), "");
return responseMsg;
} @ExceptionHandler(GlobalException.class)
@ResponseBody
public ResponseMsg HandleGlobalException(Exception e){
//只能输出捕获到的异常,未捕获到的异常不输出到日志,或者通过aop拦截器拦截所有方法
LOGGER.error(getExceptionDetail(e));
//返回失败信息
Route route = new Route();
ResponseMsg responseMsg = new ResponseMsg(route,ReturnMsgEnum.INTERNAL_ERROR.getCode(),
ReturnMsgEnum.INTERNAL_ERROR.getMsg(), "系统未捕获该异常");
return responseMsg;
} public String getExceptionDetail(Exception e) {
StringBuffer stringBuffer = new StringBuffer(e.toString() + "\n");
StackTraceElement[] messages = e.getStackTrace();
int length = messages.length;
for (int i = 0; i < length; i++) {
stringBuffer.append("\t"+messages[i].toString()+"\n");
}
return stringBuffer.toString();
} }
@RestControllerAdvice:表明他是一个Controller 并且是异常拦截的统一处理类
定义针对自定义异常的处理方法:用@ExceptionHandler(BaseException.class)注解标注
BaseException就是刚才的自定义异常
之后所有抛出的BaseException都会由他处理 自定义异常我们都能轻松捕获到了,并且输出到日志里了 如果有些异常我们没有捕获到,我们就可以定义一个切面,让所有方法都经过这个切面处理
/**
* 处理未捕获到的异常
* @ClassName: SpringAOP
* @author Mr.Chengjq
* @date 2018年10月17日
* @Description: TODO
*/
@Aspect
@Configuration
public class SpringAOP {
private static final Logger logger = LoggerFactory.getLogger(SpringAOP.class); /**
* 定义切点Pointcut
* 第一个*号:表示返回类型, *号表示所有的类型
* 第二个*号:表示类名,*号表示所有的类
* 第三个*号:表示方法名,*号表示所有的方法
* 后面括弧里面表示方法的参数,两个句点表示任何参数
*/
@Pointcut("execution(* com.example.demo..*.*(..))")
public void executionService() { } /**
* 方法调用之前调用
* @param joinPoint
*/
@Before(value = "executionService()")
public void doBefore(JoinPoint joinPoint){ //添加日志打印
String requestId = String.valueOf(UUID.randomUUID());
MDC.put("requestId",requestId);
logger.info("=====>@Before:请求参数为:{}",Arrays.toString(joinPoint.getArgs())); } /**
* 方法之后调用
* @param joinPoint
* @param returnValue 方法返回值
*/
@AfterReturning(pointcut = "executionService()",returning="returnValue")
public void doAfterReturning(JoinPoint joinPoint,Object returnValue){ logger.info("=====>@AfterReturning:响应参数为:{}",returnValue);
// 处理完请求,返回内容
MDC.clear();
} /**
* 统计方法执行耗时Around环绕通知
* @param joinPoint
* @return
*/
@Around("executionService()")
public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable{ //获取开始执行的时间
long startTime = System.currentTimeMillis(); // 定义返回对象、得到方法需要的参数
Object obj = null;
//Object[] args = joinPoint.getArgs();
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
// TODO: handle exception
logger.error(getExceptionDetail(e));
throw new GlobalException();
} // 获取执行结束的时间
long endTime = System.currentTimeMillis();
//MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 打印耗时的信息
logger.info("=====>处理本次请求共耗时:{} ms",endTime-startTime);
return obj;
} public String getExceptionDetail(Throwable e) {
StringBuffer stringBuffer = new StringBuffer(e.toString() + "\n");
StackTraceElement[] messages = e.getStackTrace();
int length = messages.length;
for (int i = 0; i < length; i++) {
stringBuffer.append("\t"+messages[i].toString()+"\n");
}
return stringBuffer.toString();
} }
这个切面里未捕获到的异常也全部做特定处理
Spring Boot与Logback的运用(自定义异常+AOP)的更多相关文章
- spring boot 中logback多环境配置
spring boot 配置logback spring boot自带了log打印功能,使用的是Commons logging 具体可以参考spring boot log 因此,我们只需要在resou ...
- spring boot 使用logback日志系统的详细说明
springboot按照profile进行打印日志 log4j logback slf4j区别? 首先谈到日志,我们可能听过log4j logback slf4j这三个名词,那么它们之间的关系是怎么样 ...
- Spring Boot 乐观锁加锁失败 - 使用AOP恢复错误
之前写了一些辅助工作相关的Spring Boot怎么使用AOP.这里继续正题,怎么减少Spring Boot 乐观锁加锁报错的情况(基本可以解决). 1. 包依赖 spring-boot-starte ...
- Spring Boot 乐观锁加锁失败 - 集成AOP
Spring Boot with AOP 手头上的项目使用了Spring Boot, 在高并发的情况下,经常出现乐观锁加锁失败的情况(OptimisticLockingFailureException ...
- 剑指架构师系列-spring boot的logback日志记录
Spring Boot集成了Logback日志系统. Logback的核心对象主要有3个:Logger.Appender.Layout 1.Logback Logger:日志的记录器 主要用于存放日志 ...
- 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
- Spring Boot之logback日志最佳实践
一.Spring Boot日志介绍 Spring Boot对所有内部日志记录使用了Commons Logging,但是底层日志实现是开放的.为Java Util日志记录.Log4J2和Logback提 ...
- 基于Spring Boot的Logback日志轮转配置
在生产环境下,日志是最好的问题调试和跟踪方法,因此日志的地位是十分重要的.我们平时经常使用的log4j,slf4j,logback等等,他们的配置上大同小异.这里就结合Spring Boot配置一下L ...
- spring boot(13)-logback和access日志
logback logback出自log4j的作者,性能和功能相比log4j作出了一些改进,而配置方法和log4j类似,是spring boot的默认日志组件.在application.propert ...
随机推荐
- 0_Simple__UnifiedMemoryStreams
使用 OpenMP 和 pthreads 两种环境,利用实现统一内存编址,计算基本的矩阵乘法 result = α * A * x + β * result . ▶ 源代码 #include < ...
- 11 并发编程-(线程)-信号量&Event&定时器
1.信号量(本质也是一把锁)Semaphore模块 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行, 信号量同一时间可以有5个任务拿到锁去执行, 如果说互斥锁是合租 ...
- JetBrains产品永久破解
吃水不忘挖井人,本博客转自:https://www.cnblogs.com/jyiqing/p/7699649.html 目的: 本人使用idea和webstorm进行开发,无奈正版实在是太贵了,只能 ...
- mysql 1292-Truncated incorrect double value
sql = "select id from company where date_year_month = %s" % "2017-3" 出错 将%s改为'%s ...
- mysql 2003: Can't connect to MySQL server on '127.0.0.1:3306' (99)
连接断开的频率太高导致报错,可以在每次连接之间sleep,或者保持一个长连接. ref:https://stackoverflow.com/questions/24884438/2003-cant-c ...
- 吴裕雄 python 数据处理(1)
import time print(time.time())print(time.localtime())print(time.strftime('%Y-%m-%d %X',time.localtim ...
- Python内置类型性能分析
Python内置类型性能分析 timeit模块 timeit模块可以用来测试一小段Python代码的执行速度. class timeit.Timer(stmt='pass', setup='pass' ...
- drupal sql 源码解析query.inc 文件
query.inc 文件: sql语句: $this->condition($field);1707 line public function condition($field, $value ...
- debug-stripped.ap_' specified for property 'resourceFile' does not exist.(转载)
1.错误描述 更新Android Studio到2.0版本后,出现了编译失败的问题,我clean project然后重新编译还是出现抑郁的问题,问题具体描述如下所示: Error:A problem ...
- new operator
[new operator] When the code new Foo(...) is executed, the following things happen: A new object is ...