并发情况下advice记录日志错乱如何解决?

问题描述

Springboot项目,在频繁调用多个接口时,advice里记录的日志会错乱。现象是这样的:在请求量大的时候,A请求正在执行未执行完毕,B请求进入并报了异常,B请求的错误日志被A记录了下来,同时A请求也莫名其妙的调用也失败了。请问如何解决?

相关代码

@Aspect
@Component
@EnableAspectJAutoProxy
public class AdviceApi {
  @Autowired
    private LogAnalyseService logAnalyseService;

    // 记录日志的对象Bean
    @Bean
    public LogChannelCall getBean() {
        return new LogChannelCall();
    }
    // 统计调用时长
    private ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Pointcut("execution(* com.api.controller.*.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String userId = request.getParameter("userId");
        String remoteAddr = request.getRemoteAddr();
     
        logChannelCall.setUserId(userId);
        logChannelCall.setIp(remoteAddr);
        startTime.set(System.currentTimeMillis());
    }

    @After("pointcut()")
    public void after() {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        boolean successful = response.getStatus() == 200;
        logChannelCall.setSuccessful(successful);
    }

    @AfterReturning("pointcut()")
    public void afterReturning() {
        logChannelCall.setSuccessful(true);
        logChannelCall.setResponseTime(System.currentTimeMillis() - startTime.get());
        logAnalyseService.insertV3Log(logChannelCall);
    }

    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void afterThrowing(Exception ex) {
        logChannelCall.setSuccessful(false);
        logChannelCall.setReason(ex.getMessage());
        logChannelCall.setResponseTime(System.currentTimeMillis() - startTime.get());
        // 执行插入日志操作
        logAnalyseService.insertV3Log(logChannelCall);
    }
}

你期待的结果是什么?实际看到的错误信息又是什么?

请大家帮我分析一下,首先如何解决日志错乱的问题、其次,为何B请求错误会导致A请求也失败呢?

阅读 2.1k
2 个回答

经过问题评论里的友♂ 好讨论,看来也不是个啥大难题,你需要的形参参数数组,这样应该可以达到你想要对应的效果了叭

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) {
     Signature signature = joinPoint.getSignature();
     MethodSignature methodSignature = (MethodSignature) signature;
     // 方法参数值
     Object[] args = joinPoint.getArgs();
     // 你要的对应参数名字
     String[] parameterNames = methodSignature.getParameterNames();
     // TODO other
}

你的日志记录对象并非线程安全对象,改为线程安全对象应该就不会出现这样的问题。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题