背景说明
之前在开发项目时,项目时一边开发 ,一边在生产环境中提供服务。由于处于开发前期,项目代码不是很健壮,再加上生产环境多变(不可控)和人员操作不当,导致异常频频发生。二生产环境 只能看到部分日志 和db,虽然可排查,但是耗时耗力,定位错误的成本非常大。于是 就想 :程序在报错时,如果可以把报错所在的类、方法、以及行数 返回给前端,这样的话,排查与定位 就会变得非常轻松,只需要根据返回的信息 ,直接找到那行代码即可。
原理
根据jvm的运行机制可知, 一个线程的执行,它所有的链路信息,都会存储在堆栈当中,包括调用关系,如:谁调用谁
开发
springboot + 自定义异常 + RestControllerAdvice全局异常处理
注: 本文的代码 , 均为模拟代码,点到为止。
自定义异常类
本文的堆栈信息获取 放在了 自定义异常类里。如需放在其它位置 ,则需要 debug : StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
这行代码 ,查看 你需要的信息 在堆栈信息数组中的位置
package com.example.myexception.excetion;
public class MyException extends RuntimeException{
private String message;
@Override
public String getMessage() {
return message;
}
public MyException() {
}
public MyException(String message) {
//获取当前线程的堆栈信息 (里面包含整个链路的调用信息 ,包括谁调用谁的关系信息)
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
//拿到 调用 此 构造方法的 上层方法的堆栈信息 下面获取数组的下标 2 ,这个下标2是经过多次debug 查找自己所需的数据在哪个位置后 才确定下来的。如果需要在其它地方获取堆栈信息 ,则请自行debug寻找下标。
StackTraceElement father = stackTrace[2];
int lineNumber = father.getLineNumber();
String className = father.getClassName();
String methodName = father.getMethodName();
String messageOfClass=",异常定位辅助信息:{类名:"+className+",方法名:"+methodName+",行数:"+lineNumber+"}";
this.message=message + messageOfClass;
}
}
返回体
package com.example.myexception.excetion;
public class RespsonseResult
{
private String CODE="异常处理状态";
private String message;
public RespsonseResult(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCODE() {
return CODE;
}
}
全局异常处理器
package com.example.myexception.excetion;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GloadExceptionHandle {
@ExceptionHandler(MyException.class)
public RespsonseResult handle(MyException mex){
return new RespsonseResult(mex.getMessage());
}
}
controller
controller层设置的返回为空,当发生异常 且被捕获时 ,则返回的对象 上面定义的返回体,差异化处理 方便做对比
package com.example.myexception.controller;
import com.example.myexception.excetion.MyException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/test")
public void test(){
try {
//模拟异常
int a=10/0;
} catch (Exception e) {
System.out.println(e);
throw new MyException(e.getMessage());
}
}
}
实测
通过postman 调用该接口 : loalhost:8080/test/test
返回体
{
"message": "/ by zero,异常定位辅助信息:{类名:com.example.myexception.controller.TestController,方法名:test,行数:19}",
"code": "异常处理状态"
}
测试结果成功反悔了 具体是代码哪一行报的问题 ,这样我们变成快速在开发环境或本地定位到问题原因, 从此告别排查难问题 (▽)