TheadlLocal用法之基于SpringAOP的全局日志跟踪

写博客的目的在于自我省,自我总结,自我梳理

烦恼:

你是否有如下烦恼:你的系统日志打印非常乱,每次排查问题需要根据业务id去查询每个对应服务中这次请求所对应的日志,在单线程情况下,还能按照业务的调用顺序去排查,但是在多线程情况下,日志可能存在乱序,排查起来非常难受。

解决:

ThreadLocal的用法,除了用于SimpleDateFormate等之外,还有更好的应用场景:在多线程情况的日志跟踪,同一线程或者一次事务的日志打印,将事务的日志保存在线程局部变量当中,在一次API请求的全链路下,均能全局的获取到这个日志的序号,无需侵入你的业务参数中层层传递。

思路:

首先你可能会想到用static的全局变量去记录日志序号,也能达到效果,但是多线程情况下同时对同一个静态变量操作时,无法确保每个线程取出的值是自己放的值。而ThreadLocal可以把变量绑定到到某一线程上,形成多线程都有自己的变量副本。

ThreadLocal的一点小分析:

先看看ThradLocal的源码:
ThradLocalMap的key为弱引用,JVM在GC时会回收,造成key=null,value=ThradLocalMap,造成内存溢出。
在这里插入图片描述

在这里插入图片描述

SpringAOP

此处用到了基于切面的几个注解,在SpringMVC的RequestMapping注解上进行拦截。

@Pointcut:切入点
@Before:前置通知
@After:后置通知

/**
 * 切面生成处理流程序列号
 **/
@Aspect
@Component
@Slf4j
public class RestSeqNumAspect {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void requestMapping() {
    }

    @Before("requestMapping()")
    public void doBefore(JoinPoint joinPoint) throws Exception {
    	ThreadLocalValueHolder.set(joinPoint.getSignature().getName()+"#"+System.currentTimeMillis()+"#"+Thread.currentThread().getId());
        log.info(" rest request[{}] begin------------------",""+ThreadLocalValueHolder.get());
    }
    @After("requestMapping()")
    public void doAfter(JoinPoint joinPoint) throws Exception {
        log.info(" rest request[{}]end------------------",""+ThreadLocalValueHolder.get());
        ThreadLocalValueHolder.set(null);
    }
}

在doAfter中需要将对象置为null,释放内存
ThreadLocal类的声明,调用其set(),get()方法就能获取到当前线程的全局变量

public class ThreadLocalValueHolder {
    public static final ThreadLocal<Object> THREAD_LOCAL = new ThreadLocal();

    public ThreadLocalValueHolder() {
    }

    public static <T> void set(T value) {
        THREAD_LOCAL.set(value);
    }

    public static <T> T get() {
        return THREAD_LOCAL.get();
    }
}
效果展示:

如图所示,我们能清楚的知道相同的日志序号是来自同一个请求,笔者只在接口的入口和出口处打印了序号,当然可以在每条日志中调用ThreadLocalValueHolder.get()进行打印,获取到的肯定是当前线程生成的序号。

在这里插入图片描述

总结:

使用ThreadLocal还能用于全局的用户信息,用户session等,把这些通用的变量或者对象从业务实体中抽离出来,减少参数的层层传递,以空间换时间,解决并发下对临界资源的访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值