线程日志标记和接口日志标记

线程维度
当我们的项目中同一时间打印的日志较多,例如同一时间有5个线程在执行,每个线程分别要打出1到20这几个数字,这样三个线程的日志就会互相穿插,除了系统自动生成的线程标识,根本不能清晰地看出具体的单个线程的执行流程。假入一个线程在执行过程中出现异常中断且不打印异常,在大量日志中就很难判断该线程在哪里中断。而系统自动生成的线程标识这东西,在实际的生产中,基本不可能用这玩意儿。所以得有另外一个方法来标记线程。
解决办法如下:
用一个系统日志类继承org.slf4j.Logger,实现所有接口方法,在所有打印的日志之前加上标记。假设我们的系统日志类叫THLogger:

public class THLogger implements org.slf4j.Logger {

    private org.slf4j.Logger logger;

    private THLogger (Class clazz) {
        logger = LoggerFactory.getLogger(clazz);
    }

    public static org.slf4j.Logger getLogger(Class clazz) {
        return new THLogger (clazz);
    }

    public String getLogIdMsg() {
        return "logTraceId[" + getUUID() + "]--";
    }

    private String getUUID() {
        return LoggerHandler.getUUID().get();
    }

    @Override
    public String getName() {
        return logger.getName();
    }

    @Override
    public boolean isTraceEnabled() {
        return logger.isTraceEnabled();
    }

    @Override
    public void trace(String msg) {
        logger.trace(getLogIdMsg() + msg);
…
}

在打印日志的时候就用THLogger的logger对象来打印,即定义logger变量如下:private static final Logger logger = THLogger.getLogger(xxx.class)
那么线程标记生成如下:

public class LoggerHandler {
    private static ThreadLocal<String> logTraceId = new ThreadLocal<String>();

    public static ThreadLocal<String> getUUID() {
        if (null == logTraceId.get()) {
            genUUID();
        }
        return logTraceId;
    }

    private static void genUUID() {
        String s = UUID.randomUUID().toString();
        //去掉“-”符号
        logTraceId.set(s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24));
    }
}

ThreadLocal为每一个线程隔离了一个uuid,打印日志的时候会调用LoggerHandler.getUUID().get()来获取当前线程的uuid标记,而有返回值的getUUID()方法会先判断当前线程的uuid是否为空,为空则为当前线程中生成一个uuid,不为空则从threadLocal容器logTraceId中取出当前线程的uuid。这样就做到了不同线程打印日志时会带上不同的uuid。
这就是以线程为维度的日志标记方法,但是以上方法有一个问题存在,那就是从threadLocal中取出当前线程的uuid,也就是通过logTraceId.get()获取当前线程的uuid时,会根据系统默认生成的线程名来做当前线程的判断。这个是什么意思呢,举个栗子:假如系统生成一个线程叫http-bio-8088-exec-1,第二个线程叫http-bio-8088-exec-2,依次类推,直到系统默认的最大值。然后线程1跑一段时间之后销毁了。接下来系统将会重新利用http-bio-8088-exec-1来命名新的线程。也就是说系统的默认命名方式是重复利用的。因此第二个http-bio-8088-exec-1线程还会继续用第一个http-bio-8088-exec-1的标记,因为取uuid的时候会默认这两个线程是同一个。

接口维度
将LoggerHandler稍微改造一下,将其作为一个过滤器:

public class LogFilter extends OncePerRequestFilter {
    private static ThreadLocal<String> logTraceId = new ThreadLocal<String>();
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        genUUID();
        filterChain.doFilter(request, response);
    }
    public static ThreadLocal<String> getUUID() {
        if (null == logTraceId.get()) {
            genUUID();
        }
        return logTraceId;
    }


    private static void genUUID() {
        String s = UUID.randomUUID().toString();
        //去掉“-”符号
        logTraceId.set(s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24));
    }
}

这样每次接口请求都会执行过滤方法,先为当前线程生成uuid保存到threadlocal中。这样就算第二个http-bio-8088-exec-1线程启动了,也会生成不同的uuid。此时每个接口都有一个uuid标记,标识一次接口请求中打印的全部日志。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值