请求外部系统的日志记录方式

背景

在系统中对外部系统发起HTTP请求时,需要记录所有失败和异常的相关请求信息至本地,在实现阶段设计两套方式来实现,一种是利用自定义注解的方式,以切面的方式来实现。另一种是在使用mybatisPlus的时候将请求的方法,在进行一次包装(父类或者单独包装为一个bean都可以),在使用时只需要调用包装后的方法即可。

使用AOP切面的方法

定义两个自定义注解@RequestLog、@ParameterType
@RequestLog:作用于具体的HTTP方法上,标记切入点。
@ParameterType:作用于方法参数上,用户获取日志需要的入参。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLog {
    RequestTypeEnum requestMethod() default RequestTypeEnum.GET;
}
复制代码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLog {
    RequestTypeEnum requestMethod() default RequestTypeEnum.GET;
}
复制代码

定义请求方式以及参数类型

@AllArgsConstructor
@Getter
public enum RequestTypeEnum {
    /**
     *请求方式
     */

    GET,
    POST,
    PUT,
    DELETE;

}
复制代码
@AllArgsConstructor
@Getter
public enum  ParameterTypeEnum {
    /**
     *请求参数类型
     */

    URL,
    DATA,
    HEADER,
    CONFIG;
}
复制代码

定义切点,将自定义注解作用于方法上

@RequestLog(requestMethod = RequestTypeEnum.POST)
public  JSONObject httpSmartPostJson(@ParameterType(parameterType = ParameterTypeEnum.URL) String url,
                                           @ParameterType(parameterType = ParameterTypeEnum.DATA) String paramJson,
                                           @ParameterType(parameterType = ParameterTypeEnum.CONFIG) SmartOutboundConfig.ServiceInfo serviceInfo) {
                                           //具体调用业务}
复制代码

定义切面

//配置切面
@Component
@Aspect
@Slf4j
@Order(1)
public class RequestLogAspect {

    @Autowired
    private SmartLogService smartLogService;

    /**
     * 配置切入点
     */
    @Pointcut(value = "@annotation(com.gw.scrm.outbound.constants.annotation.RequestLog)")
    public void requestAspect() {

    }
    
    //选择切入方式
    @Around("requestAspect()")
    public Object handle(ProceedingJoinPoint joinPoint) {
        SmartLog smartLog = new SmartLog();
        Object proceed = null;
        try {
            //获取签名以及方法、参数信息
            Signature signature = joinPoint.getSignature();
            //格式化为MethodSignature
            MethodSignature methodSignature = (MethodSignature) signature;
            //获取目标方法
            Method targetMethod = methodSignature.getMethod();
            //获取目标方法上的注解
            RequestLog annotation = targetMethod.getAnnotation(RequestLog.class);
            if (annotation != null) {
                smartLog.setRequestType(annotation.requestMethod().name());
            }
            //获取目标方法的参数注解,1维是参数,2维是注解
            Annotation[][] annotations = targetMethod.getParameterAnnotations();
            //获取参数
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < annotations.length; i++) {
                Object param = args[i];
                Annotation[] paramAnn = annotations[i];
                //参数为空,直接下一个参数
                if (param == null || paramAnn.length == 0) {
                    continue;
                }
                for (Annotation paramAnnotation : paramAnn) {
                    //这里判断当前注解是否为ParameterType
                    if (paramAnnotation instanceof ParameterType) {
                        //获取参数类型
                        ParameterTypeEnum parameterType = ((ParameterType) paramAnnotation).parameterType();
                        //根据注解中的值获取需要的参数
                        if (parameterType == ParameterTypeEnum.URL) {
                            smartLog.setRequestUrl(String.valueOf(param));
                        } else if (parameterType == ParameterTypeEnum.DATA) {
                            smartLog.setRequestParameter(String.valueOf(param));
                        }
                    }
                }
            }
            //获取返回值
            proceed = joinPoint.proceed();
            if (proceed != null) {
                Integer returnCode = JSON.parseObject(proceed.toString()).getInteger(RETURN_CODE);
                //判断是否失败,失败则保存记录至本地
                if (!ResultEnum.isSuccess(returnCode)) {
                    //请求失败
                    smartLog.setStatue(1);
                    smartLog.setResponseResult(proceed.toString());
                    smartLogService.save(smartLog);
                }
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //异常信息也记录至本地
            smartLog.setStatue(2);
            smartLog.setExceptionInfo(throwable.toString());
            smartLogService.save(smartLog);
        }
        return proceed;
    }
}
复制代码

对请求方法包装的方法

通过在父类中实现mybatisPlus的Service层,并在父类中调用HTTP工具类的请求方法,并对该方法添加日志接口,各个具体实现层继承该父类。
创建父类实现mybatisPlus的Service层

@Slf4j
public class BaseService<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements IService<T> {

    @Autowired
    private SmartLogService smartLogService;

    public JSONObject invokeWithLog(String url, SmartOutboundConfig.ServiceInfo serviceInfo) {
        return invokeWithLog(url, null, serviceInfo);
    }
    //记录日志
    public JSONObject invokeWithLog(String url, String paramJson, SmartOutboundConfig.ServiceInfo serviceInfo) {
        JSONObject jsonObject = null;
        SmartLog smartLog = new SmartLog();
        smartLog.setRequestType("POST");
        smartLog.setRequestUrl(url);
        smartLog.setRequestParameter(paramJson);
        try {
            jsonObject = HttpUtil.httpSmartPostJson(url,paramJson, serviceInfo);
            if (jsonObject != null) {
                Integer returnCode = JSON.parseObject(jsonObject.toString()).getInteger(RETURN_CODE);
                if (!ResultEnum.isSuccess(returnCode)) {
                    //请求失败
                    smartLog.setStatue(1);
                    smartLog.setResponseResult(jsonObject.toString());
                    smartLogService.save(smartLog);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            smartLog.setStatue(2);
            smartLog.setExceptionInfo(e.toString());
            smartLogService.save(smartLog);
        }
        return jsonObject;
    }
}
复制代码

实现类继承BaseService

@Service
@Slf4j
public class SmartClueService extends BaseService<SmartClueMapper, SmartClue> {

    @Autowired
    SmartOutboundConfig smartOutboundConfig;

    @Autowired
    SmartDealerShopService smartDealerShopService;


    public GenerateTelRes generateSmartPhone(GenerateSmartPhoneReq req) {
    //调用父类中的invokeWithLog()方法实现日志记录
    invokeWithLog( url,  paramJson, config) {
    }
}
复制代码

猜你喜欢

转载自juejin.im/post/7037403065166610445