SpringBoot AOP异常日志处理 使用AOP+注解的方式进行异常日志的处理
最近公司的一个项目需要将异常日志通过企业微信进行告警,由于消息推送已经有异常处理平台进行处理,现在只需要捕获异常信息,将信息发送到异常处理平台就可以了。可以选择的方案其实有两种,一个是springboot其实有全局异常处理,捕获到异常后可以进行消息推送。另一个就是通过AOP进行处理。因为全局异常处理不够灵活,比如不同的方法可能需要处理的异常类型不同,对不同的方法可能会有特殊的处理等等,最终选择了使用AOP+注解的方式进行异常日志的处理。
异常日志注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ExceptionLogService {
/**
* 系统模块名
* @return
*/
String moduleName() default "";
/**
* 业务名称
* @return
*/
String businessName() default "";
/**
* 日志前缀
* @return
*/
String messagePrefix() default "";
/**
* 需要推送消息的异常类型
* @return
*/
Class<? extends Throwable>[] exception() default {};
}
参数moduleName
和businessName
主要是为了定位异常位置,可写可不写,messagePrefix
可以理解为对异常的说明,同时可以指定需要进行异常日志推送的异常类型,如果不指定则所有的异常都会进行推送。
切面类
@Aspect
@Component
public class ExceptionLogServiceAspect implements Ordered {
private final static Logger logger= LoggerFactory.getLogger(ExceptionLogServiceAspect.class);
@Autowired
MessageService messageService;
@Pointcut("@annotation(com.test.ExceptionLogService)")
public void exceptionLogService(){
}
@Around("exceptionLogService()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result=null;
Throwable exception=null;
MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature();
ExceptionLogService annotation = signature.getMethod().getAnnotation(ExceptionLogService.class);
String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();
String methodName = signature.getName();
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
//获取需要推送消息的异常类型
exception = getLogException(annotation, throwable);
//构建消息内容
String logContent = buildLogContent(annotation,className,methodName, exception);
//推送消息到异常处理平台
messageService.sendErrorMessage(logContent);
//消息推送完成后将异常抛出,因为对于异常有另外的AOP进行处理,所以只是处理异常日志,然后直接抛出
exception=throwable;
}finally {
if (exception!=null){
throw exception;
}
}
return result;
}
/**
* 根据ExceptionLogService 注解中的异常类型数组获取需要推送消息的异常类型,如果数组为空则所有的异常都进行消息推送
* @param annotation
* @param throwable
* @return
*/
private Throwable getLogException( ExceptionLogService annotation,Throwable throwable){
Class<?>[] exceptions = annotation.exception();
Throwable exception=null;
if (exceptions==null || exceptions.length==0){
exception=throwable;
}else {
for (Class<?> clazz : exceptions) {
if (clazz.isInstance(throwable)){
exception=throwable;
break;
}
}
}
return exception;
}
/**
* 构建异常消息内容
* @param annotation ExceptionLogService注解
* @param className 发生异常的类名称
* @param methodName 发生异常的方法名称
* @param throwable 抛出异常
* @return 消息内容
*/
private String buildLogContent(ExceptionLogService annotation, String className, String methodName, Throwable throwable){
if (annotation==null||throwable==null){
return null;
}
String moduleName = annotation.moduleName();
String businessName = annotation.businessName();
String messagePrefix = annotation.messagePrefix();
StringBuilder stringBuilder = new StringBuilder();
if (StringUtils.isNotBlank(moduleName)){
stringBuilder.append(moduleName).append(" 模块,");
}
if (StringUtils.isNotBlank(businessName)){
stringBuilder.append(businessName).append(" 业务");
}
//如果异常消息为空,展示异常类型
String message = throwable.getMessage();
boolean isNull=false;
if (StringUtils.isBlank(message)){
message = throwable.getClass().getSimpleName();
isNull=true;
}
stringBuilder.append("发生异常:").append(StringUtils.isBlank(messagePrefix) ? "" : messagePrefix);
stringBuilder.append(isNull ? " 异常类型为:" : " 异常消息为:")
.append(message).append("。异常位置:").append(className)
.append(".class >>> ").append(methodName).append("()");
String logContent = stringBuilder.toString();
logger.error("AOP异常日志:{}",logContent);
return logContent;
}
@Override
public int getOrder() {
return 1;
}
}
异常消息格式
moduleName 模块,businessName 业务发生异常,异常消息为:异常消息。异常位置:类名.class>>>方法名()
对于异常消息为空的异常将展示异常的类型。
测试
@ExceptionLogService(moduleName = "测试模块",businessName = "test",exception = {NullPointerException.class})
@Override
public String logServiceTest() throws Exception {
logger.info("testLog");
throw new NullPointerException();
return "test";
}
最近公司的一个项目需要将异常日志通过企业微信进行告警,由于消息推送已经有异常处理平台进行处理,现在只需要捕获异常信息,将信息发送到异常处理平台就可以了。可以选择的方案其实有两种,一个是springboot其实有全局异常处理,捕获到异常后可以进行消息推送。另一个就是通过AOP进行处理。因为全局异常处理不够灵活,比如不同的方法可能需要处理的异常类型不同,对不同的方法可能会有特殊的处理等等,最终选择了使用AOP+注解的方式进行异常日志的处理。
异常日志注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ExceptionLogService {
/**
* 系统模块名
* @return
*/
String moduleName() default "";
/**
* 业务名称
* @return
*/
String businessName() default "";
/**
* 日志前缀
* @return
*/
String messagePrefix() default "";
/**
* 需要推送消息的异常类型
* @return
*/
Class<? extends Throwable>[] exception() default {};
}
参数moduleName
和businessName
主要是为了定位异常位置,可写可不写,messagePrefix
可以理解为对异常的说明,同时可以指定需要进行异常日志推送的异常类型,如果不指定则所有的异常都会进行推送。
切面类
@Aspect
@Component
public class ExceptionLogServiceAspect implements Ordered {
private final static Logger logger= LoggerFactory.getLogger(ExceptionLogServiceAspect.class);
@Autowired
MessageService messageService;
@Pointcut("@annotation(com.test.ExceptionLogService)")
public void exceptionLogService(){
}
@Around("exceptionLogService()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result=null;
Throwable exception=null;
MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature();
ExceptionLogService annotation = signature.getMethod().getAnnotation(ExceptionLogService.class);
String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();
String methodName = signature.getName();
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
//获取需要推送消息的异常类型
exception = getLogException(annotation, throwable);
//构建消息内容
String logContent = buildLogContent(annotation,className,methodName, exception);
//推送消息到异常处理平台
messageService.sendErrorMessage(logContent);
//消息推送完成后将异常抛出,因为对于异常有另外的AOP进行处理,所以只是处理异常日志,然后直接抛出
exception=throwable;
}finally {
if (exception!=null){
throw exception;
}
}
return result;
}
/**
* 根据ExceptionLogService 注解中的异常类型数组获取需要推送消息的异常类型,如果数组为空则所有的异常都进行消息推送
* @param annotation
* @param throwable
* @return
*/
private Throwable getLogException( ExceptionLogService annotation,Throwable throwable){
Class<?>[] exceptions = annotation.exception();
Throwable exception=null;
if (exceptions==null || exceptions.length==0){
exception=throwable;
}else {
for (Class<?> clazz : exceptions) {
if (clazz.isInstance(throwable)){
exception=throwable;
break;
}
}
}
return exception;
}
/**
* 构建异常消息内容
* @param annotation ExceptionLogService注解
* @param className 发生异常的类名称
* @param methodName 发生异常的方法名称
* @param throwable 抛出异常
* @return 消息内容
*/
private String buildLogContent(ExceptionLogService annotation, String className, String methodName, Throwable throwable){
if (annotation==null||throwable==null){
return null;
}
String moduleName = annotation.moduleName();
String businessName = annotation.businessName();
String messagePrefix = annotation.messagePrefix();
StringBuilder stringBuilder = new StringBuilder();
if (StringUtils.isNotBlank(moduleName)){
stringBuilder.append(moduleName).append(" 模块,");
}
if (StringUtils.isNotBlank(businessName)){
stringBuilder.append(businessName).append(" 业务");
}
//如果异常消息为空,展示异常类型
String message = throwable.getMessage();
boolean isNull=false;
if (StringUtils.isBlank(message)){
message = throwable.getClass().getSimpleName();
isNull=true;
}
stringBuilder.append("发生异常:").append(StringUtils.isBlank(messagePrefix) ? "" : messagePrefix);
stringBuilder.append(isNull ? " 异常类型为:" : " 异常消息为:")
.append(message).append("。异常位置:").append(className)
.append(".class >>> ").append(methodName).append("()");
String logContent = stringBuilder.toString();
logger.error("AOP异常日志:{}",logContent);
return logContent;
}
@Override
public int getOrder() {
return 1;
}
}
异常消息格式
moduleName 模块,businessName 业务发生异常,异常消息为:异常消息。异常位置:类名.class>>>方法名()
对于异常消息为空的异常将展示异常的类型。
测试
@ExceptionLogService(moduleName = "测试模块",businessName = "test",exception = {NullPointerException.class})
@Override
public String logServiceTest() throws Exception {
logger.info("testLog");
throw new NullPointerException();
return "test";
}