2022-6月更文挑战1-使用@Aspect注解进行接口日志统一记录

143 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

使用@Aspect注解进行接口日志统一记录

前文

本文内容为采用@Aspect注解进行日志记录的一次实现方案,以及对于其中一些出现问题的记录。

方案实现逻辑

本方案的实现逻辑主要是采用@Aspect注解在各服务中进行切面处理。需要进行记录的接口采用特定的注解进行标记,在切面类中对于该类接口进行切面编程。而在切点的处理逻辑中,在切点中获取请求所携带的信息。由于存在网关服务,因此ip不能通过切面中直接获取,而是选择在网关中进行预先的处理。在网关中获取来自客户端真实的ip,并将ip存储到后续请求的header中。实际的切点处则采用请求中所携带的ip作为真实的请求ip。如果直接在各服务进行ip的获取,会出现获取的ip是网关ip而不是实际请求ip的问题。获取到日志数据后,将其通过kafka消息队列写入,又专门的日志处理服务进行后续的操作处理。

方案代码

@Aspect
@Component
public class ReqAspect {
    private static Logger logger = LoggerFactory.getLogger(ReqAspect.class);
    private String requestHeadClientIp = "request_header_client_ip";

    @Value("${spring.kafka.api_log_topic}")
    private String logTopic;

    @Pointcut("@annotation(cc.crrc.business.annotation.ApiReqAspect)")
    public void cutPoint(){

    }

    @Around(value = "cutPoint()  && @annotation(apiReqAspect)",argNames = "proceedingJoinPoint,apiReqAspect")
    public Object processAround(ProceedingJoinPoint proceedingJoinPoint, ApiReqAspect apiReqAspect) throws Exception {
        Object ret = null;
        ApiLogPO apiLogPO = new ApiLogPO();
        String ip = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader(requestHeadClientIp);
        long start = System.currentTimeMillis();
        long end = System.currentTimeMillis();
        int code;
        try {
            ret = proceedingJoinPoint.proceed();
            end = System.currentTimeMillis();
            if(ret instanceof Result){
                if(((Result)ret).isSuccess()){
                    code = HttpStatus.OK.value();
                }else{
                    code = Integer.parseInt((((Result)ret).getError()).getCode());
                }
            }else if(ret instanceof LinkedHashMap){
                LinkedHashMap<String,Object> data = (LinkedHashMap<String,Object>)ret;
                if((Boolean) data.get("success") == false){
                    code = Integer.parseInt(String.valueOf(((LinkedHashMap<String,Object>)data.get("error")).get("code")));
                }else{
                    code = HttpStatus.OK.value();
                }
            }else{
                code = HttpStatus.OK.value();
            }
        } catch (Exception e) {
            end = System.currentTimeMillis();
            if(e instanceof RestApiException){
                code = Integer.valueOf(((RestApiException) e).getErrorCode());
            }else{
                code = HttpStatus.INTERNAL_SERVER_ERROR.value();
            }
            apiLogPO.setDuration(end - start);
            apiLogPO.setStatus(code);
            KafkaUtil.sendMessageSync(logTopic, JsonUtils.serialize(apiLogPO));
            throw e;
        } catch (Throwable throwable) {
            code = HttpStatus.INTERNAL_SERVER_ERROR.value();
            throwable.printStackTrace();
        }
        apiLogPO.setDuration(end -start);
        apiLogPO.setStatus(code);
        try {
            KafkaUtil.sendMessageSync(logTopic, JsonUtils.serialize(apiLogPO));
        }catch (Exception e){
        }
        return ret;
    }
}

由于篇幅所限,部分代码存在删减。

注意点

需要注意的是,切点中proceedingJoinPoint.proceed()属于后续逻辑的实际执行,不可反复调用。如果需要对结果进行判断,要实现将其存储于变量中操作。

后记

  • 千古兴亡多少事?悠悠。不尽长江滚滚流。