java使用注解记录异常方法日志

时间: 2023-12-21 admin 维修知识

java使用注解记录异常方法日志

java使用注解记录异常方法日志

一,背景

在开发过程,如订单创建逻辑,如果方法异常,需要记录相关的日志信息,比如一些重要的业务信息,及异常信息

二,实现技术方案

1,自定义注解

2,AOP拦截注解,后置异常通知

3,使用ThreadLocal,定义,异常单 全局变量,负责不同类的参数传递(应该是方法的参数传递,同一个方法多线程访问,记录线程级别访问数据)

三,代码

1,自定义注解

package com.smcv.yl.order.service.annotation;import com.smcv.yl.order.service.constant.OrderExceptionType;import java.lang.annotation.*;/*** @author zjq*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OrderExceptionAnnotation {/*** 异常单类型* 可以不用指定,默认其他异常*/OrderExceptionType orderException() default OrderExceptionType.OTHER_EXCEPTION;/*** 请求快照入参*/String requestSnapshot() default "";/*** 运力订单号*/String ylOrderNo() default "";/*** 第三方订单号*/String outOrderNo() default "";/*** 订单来源*/String sourceApp();}

2.上下文参数传递类

package com.smcv.yl.order.service.service.other;import com.alibaba.fastjson.JSON;
import org.apachemons.lang3.StringUtils;import java.util.HashMap;
import java.util.Map;/**** 描述:异常单 全局变量,负责不同类的参数传递*/
public class OrderThreadLocalContext {private static final ThreadLocal<Map<String, String>> MAP_THREAD_LOCAL = ThreadLocal.withInitial(HashMap::new);public static final String YL_ORDER_NO = "ylOrderNo";public static final String OUT_ORDER_NO = "outOrderNo";public static final String REQUEST_SNAPSHOT = "requestSnapshot";protected static ThreadLocal<Map<String, String>> getMapThreadLocal() {return MAP_THREAD_LOCAL;}public static Map<String, String> getMap() {return MAP_THREAD_LOCAL.get();}public static void put(String key, String value) {getMap().put(key, value);}public static String get(String key) {return getMap().get(key);}public static void remove() {MAP_THREAD_LOCAL.remove();}public static void setYlOrderNo(String ylOrderNo) {setOrderNoAndRequestSnapshot(ylOrderNo, null, null);}public static void setOutOrderNo(String outOrderNo) {setOrderNoAndRequestSnapshot(null, outOrderNo, null);}public static void setRequestSnapshot(Object requestSnapshot) {setOrderNoAndRequestSnapshot(null, null, requestSnapshot);}public static void setOrderNoAndRequestSnapshot(String ylOrderNo, String outOrderNo, Object requestSnapshot) {try {if (StringUtils.isNotBlank(ylOrderNo)) {put(YL_ORDER_NO, ylOrderNo);}if (StringUtils.isNotBlank(outOrderNo)) {put(OUT_ORDER_NO, outOrderNo);}if (null != requestSnapshot) {put(REQUEST_SNAPSHOT, JSON.toJSONString(requestSnapshot));}} catch (Exception e) {e.printStackTrace();}}
}

3.AOP类

package com.smcv.yl.order.service.aop;import com.smcv.yl.order.service.service.async.AsyncOrderExceptionHandle;
import com.smcv.yl.order.service.service.other.OrderThreadLocalContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.expression.spel.standard.SpelExpressionParser;/*** @author zjq*/
@Order
@Aspect
@Configuration
@Slf4j
public class OrderExceptionAspect {public static final String YL_ORDER_NO = "ylOrderNo";public static final String OUT_ORDER_NO = "outOrderNo";public static final String REQUEST_SNAPSHOT = "requestSnapshot";@Autowiredprivate AsyncOrderExceptionHandle asyncExceptionHandle;public OrderExceptionAspect() {}@Pointcut("@annotation(com.smcv.yl.order.service.annotation.OrderExceptionAnnotation)")public void orderExceptionAnnotation() {}/*** 后置异常通知*/@AfterThrowing(value = "orderExceptionAnnotation()", throwing = "throwable")public void exceptionHandle(JoinPoint joinPoint, Throwable throwable) throws Exception {String ylOrderNo = OrderThreadLocalContext.get(YL_ORDER_NO);String outOrderNo = OrderThreadLocalContext.get(OUT_ORDER_NO);String s = OrderThreadLocalContext.get(REQUEST_SNAPSHOT);//这里异步处理防止异常影响主业务asyncExceptionHandle.asyncExceptionHandle(joinPoint, throwable, ylOrderNo, outOrderNo, s);//清除当前线程绑定的ThreadLocal,防止可能存在的内存泄露// 防止web服务线程复用导致前一个请求的副本变量被下一个线程所获取OrderThreadLocalContext.remove();}// 在目标方法返回结果时后执行(当方法抛出异常时,无法执行)。// 调用成功无异常请求最后调用此方法清除线程绑定的副本变量@AfterReturning(value = "orderExceptionAnnotation()")public void afterReturningMethod() {//清除当前线程绑定的ThreadLocal,防止可能存在的内存泄露// 防止web服务线程复用导致前一个请求的副本变量被下一个线程所获取OrderThreadLocalContext.remove();}@Bean@ConditionalOnMissingBean(SpelExpressionParser.class)public SpelExpressionParser spelExpressionParser() {return new SpelExpressionParser();}}

4.业务方法

  @Override@OrderExceptionAnnotation(requestSnapshot = "#receiveOrder.orderInfo",sourceApp = Constant.WX)public OrderResponse<ESBOrderResponse> commonCreateOrder(ReceiveOrder receiveOrder) {ESBOrderInfoReq orderInfo = JSON.parseObject(JSON.toJSONString(receiveOrder.getOrderInfo()), new TypeReference<ESBOrderInfoReq>() {});ESBRoute route = orderInfo.getRoute();ESBOrderDTO esbOrder = orderInfo.getEsbOrder();//异常单业务逻辑,异常感知,全局变量输入OrderThreadLocalContext.setOutOrderNo(esbOrder.getOutOrderNo());