public class Main {
private static final String KEY ="requestId";
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args){// 入口传入请求ID
MDC.put(KEY, UUID.randomUUID().toString());// 打印日志
logger.debug("log in main thread 1");
logger.debug("log in main thread 2");
logger.debug("log in main thread 3");// 出口移除请求ID
MDC.remove(KEY);}}
2018-02-1713:19:52.606{requestId=f97ea0fb-2a43-40f4-a3e8-711f776857d0}[main] DEBUG cn.wudashan.Main- log in main thread 12018-02-1713:19:52.609{requestId=f97ea0fb-2a43-40f4-a3e8-711f776857d0}[main] DEBUG cn.wudashan.Main- log in main thread 22018-02-1713:19:52.609{requestId=f97ea0fb-2a43-40f4-a3e8-711f776857d0}[main] DEBUG cn.wudashan.Main- log in main thread 3
然而,MDC工具真的有我们所想的这么方便吗?回到我们开头,一次请求可能涉及多线程异步处理,那么在多线程异步的场景下,它是否还能正常运作呢?Talk is cheap, show me the code。
public class Main {
private static final String KEY ="requestId";
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args){// 入口传入请求ID
MDC.put(KEY, UUID.randomUUID().toString());// 主线程打印<font style="color: #1e6bb8;word-wrap: break-word;font-weight: bold;border-bottom: 1px solid">日志</font>
logger.debug("log in main thread");// 异步线程打印<font style="color: #1e6bb8;word-wrap: break-word;font-weight: bold;border-bottom: 1px solid">日志</font>
new Thread(new Runnable(){
@Override
public void run(){
logger.debug("log in other thread");}}).start();// 出口移除请求ID
MDC.remove(KEY);}}
2018-02-1714:05:43.487{requestId=e6099c85-72be-4986-8a28-de6bb2e52b01}[main] DEBUG cn.wudashan.Main- log in main thread
2018-02-1714:05:43.490{}[Thread-1] DEBUG cn.wudashan.Main- log in other thread
public class MDCRunnable implements Runnable {
private final Runnable runnable;
private final Map<String, String> map;
public MDCRunnable(Runnable runnable){
this.runnable= runnable;// 保存当前线程的MDC值
this.map= MDC.getCopyOfContextMap();}
@Override
public void run(){// 传入已保存的MDC值
for (Map.Entry<String, String> entry : map.entrySet()){
MDC.put(entry.getKey(), entry.getValue());}// 装饰器模式,执行run方法
runnable.run();// 移除已保存的MDC值
for (Map.Entry<String, String> entry : map.entrySet()){
MDC.remove(entry.getKey());}}}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
接着,我们需要对main函数里创建的Runnable实现类进行装饰:
public class Main {
private static final String KEY ="requestId";
private static final Logger logger = LoggerFactory.getLogger(Main.class);
private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
public static void main(String[] args){// 入口传入请求ID
MDC.put(KEY, UUID.randomUUID().toString());// 主线程打印日志
logger.debug("log in main thread");// 异步线程打印日志,用MDCRunnable装饰Runnable
new Thread(new MDCRunnable(new Runnable(){
@Override
public void run(){
logger.debug("log in other thread");}})).start();// 异步线程池打印日志,用MDCRunnable装饰Runnable
EXECUTOR.execute(new MDCRunnable(new Runnable(){
@Override
public void run(){
logger.debug("log in other thread pool");}}));
EXECUTOR.shutdown();// 出口移除请求ID
MDC.remove(KEY);}}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
执行main函数,将会输出以下日志:
2018-03-0423:44:05.343{requestId=5ee2a117-e090-41d8-977b-cef5dea09d34}[main] DEBUG cn.wudashan.Main- log in main thread
2018-03-0423:44:05.346{requestId=5ee2a117-e090-41d8-977b-cef5dea09d34}[Thread-1] DEBUG cn.wudashan.Main- log in other thread
2018-03-0423:44:05.347{requestId=5ee2a117-e090-41d8-977b-cef5dea09d34}[pool-2-thread-1] DEBUG cn.wudashan.Main- log in other thread pool