天天看點

異步線程裡的日志不好追蹤?小支一招,輕松搞定!

衆所周知,通過唯一的鍊路id來追蹤一次請求的所有日志,對于排查生産問題來說,會是非常給力的。

這個比較容易實作。我之前的部落格也有多次提及。

那麼,如果涉及到異步線程處理的話,我們知道,由于異步線程與工作線程是兩個不同的線程,是以,這時的線程名會發生變化。一次請求的完整日志就無法通過唯一的辨別來過濾了。

有沒有辦法呢?

問題即答案。當然是有的。

線程是用來執行任務的,任務是一段程式代碼的封裝。在java中,任務通過 java.lang.Runnable 來表示。使用方面,我們可以自己定義一個實作Runnable的任務類,也可以用lambda表達式的方式直接使用Runnable,來作為線程或線程池的參數。

自定義實作了Runnable的類來使用異步線程,先貼demo代碼。

實作了Runnable接口的類--MyTask:

package com.emaxcard.test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyTask implements Runnable {
    String flag;

    public MyTask(String flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        log.info("這是異步線程裡的日志。參數flag={}", flag);
    }
}      

View Code

主線程測試類:

package com.emaxcard.test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RunnableTest {
    public static void main(String[] args) {
        log.info("主線程begin");
        new Thread(new MyTask("參數值test")).start();
        log.info("主線程end");
    }

}      

View Code

列印出來的log如下:

16:11:27.613 [main] INFO com.emaxcard.test.RunnableTest - 主線程begin

16:11:27.629 [main] INFO com.emaxcard.test.RunnableTest - 主線程end

16:11:27.630 [Thread-0] INFO com.emaxcard.test.MyTask - 這是異步線程裡的日志。參數flag=參數值test

我們希望看到的日志是:

16:11:27.613 [main] INFO com.emaxcard.test.RunnableTest - 主線程begin

16:11:27.629 [main] INFO com.emaxcard.test.RunnableTest - 主線程end

16:11:27.630 [main] INFO com.emaxcard.test.MyTask - 這是異步線程裡的日志。參數flag=參數值test

小支一招,輕松搞定。給 MyTask 加點料,代碼如下。這樣就能實作?是的,因為構造MyTask對象的操作發生在目前工作線程,也就是說,MyTask構造器的代碼是在工作線程裡執行的。沒錯,正是基于這一點。

package com.emaxcard.test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyTask implements Runnable {
    String flag;
    String threadName;

    public MyTask(String flag) {
        this.flag = flag;
        this.threadName = Thread.currentThread().getName();
    }

    @Override
    public void run() {
        Thread.currentThread().setName(threadName);
        log.info("這是異步線程裡的日志。參數flag={}", flag);
    }
}      
package com.emaxcard.test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RunnableTest {
    public static void main(String[] args) {
        log.info("主線程begin");
        String name = Thread.currentThread().getName();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Thread.currentThread().setName(name);
                log.info("這是異步線程裡的日志");
            }
        }).start();
        log.info("主線程end");
    }

}      

繼續閱讀