因业务的需要,需要实现对后端进行统一异常处理,并返回统一的返回值;并将异常信息需要保存到今天的文本日志中;冲冲冲,步骤如下,跟着我走就能实现:
第一步:
在springboot项目的resources目录下新建file文件,文件名为:logback-spring.xml该文件的目录如下:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="consoleApp" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>
%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method:%L -%msg%n
</pattern>
</layout>
</appender>
<appender name="fileInfoApp" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>
%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method:%L -%msg%n
</pattern>
</encoder>
<!-- 滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 路径 -->
<fileNamePattern>./app_log/log/app.info.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<appender name="fileErrorApp" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>
%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method:%L -%msg%n
</pattern>
</encoder>
<!-- 设置滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 路径 -->
<fileNamePattern>app_log/log/app.err.%d.log</fileNamePattern>
<!-- 控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,
且<maxHistory> 是1,则只保存最近1个月的文件,删除之前的旧文件 -->
<MaxHistory>1</MaxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="consoleApp"/>
<appender-ref ref="fileInfoApp"/>
<appender-ref ref="fileErrorApp"/>
</root>
</configuration>
这样就会在项目组的当前文件实现这个文件目录,每天还会生成两个文件一个是error文件,一个是info文件,后面统一异常的信息就会出现在这个error文件里面;
第二步:利用springboot的aop特性进行切入,首先pom文件增加如果代码
<!--使用AspectJ方式注解需要相应的包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第三步:新建Log接口类,代码如下:
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Log {
}
这个的作用是自定义的一个注解,用于后面实现的功能是只需要我们在要实现统一异常的方法上加上@Log这个注解就可以实现.好了,接下来我们就利用aop来切进去了
第四步:新建切面类,我这里使用的是ViewApect.java类,代码如下:
@Aspect
@Component
public class ViewAspect {
private static final Logger log= LoggerFactory.getLogger(ViewAspect.class);
@Pointcut("@annotation(com.xxxx.demo.config.Log)")
public void logPointcut(){}
@Around("logPointcut()")
public Object logHandler(ProceedingJoinPoint process) throws Throwable{
String exception="";
long startTime=System.currentTimeMillis();
MethodSignature methodSignature= (MethodSignature) process.getSignature();
Method method=methodSignature.getMethod();
String methodName=method.getName();
String className= method.getDeclaringClass().getName();
Object[] args=process.getArgs();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
StringBuilder params=new StringBuilder();
for (int i = 0; i < args.length; i++) {
params.append(args[i].toString());
params.append(";");
}
Object result= null;
try {
result = process.proceed();
System.out.println(result);
} catch (Exception throwable) {
SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String catchtime= df1.format(new Date());
long costTime=System.currentTimeMillis()-startTime;
log.error("请求时间:{},请求耗时:{},请求类名:{},请求方法:{},请求参数:{},请求结果:{}",catchtime,costTime,className,methodName,params.toString(),throwable.getClass()+":"+throwable.getMessage());
for (int i=0;i<throwable.getStackTrace().length;i++){
log.error(String.valueOf(throwable.getStackTrace()[i]));
}
}
finally {
Map<String,Object>map=new HashMap<>();
return map;
}
}
}
这里的finally里面的返回值,这个根据你们需要进行改造,好了,这样就可以实现了,只需要在方法上面增加@Log注解,就可以了;
注意:我这里用的是throwable.getStackTrace()这个方法来获取异常信息,这是个坑来着,这个返回的是数组来着,需要对该数组进行遍历,我看网上使用的很多都是throwable.getMessage()这个,这个只能获取到报错信息的第一行,虽然说这个能知道报错的大概信息,但是对于调试的话就很难实现了;因为没有具体的异常信息;而.getStackTrace()这个恰恰就刚好从异常的第二行开始进行的,所以结合getMessage()和getStackTrace()就可以实现完整的异常信息的记录;之前使用的是throwable.getCause().getMessage(),这个方法一搞,返回直接500了,所以这个不可取;调试到最后使用的是这个方法
ps:如果有更好的方法,欢迎交流.