最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java 如何實現日志追蹤MDC

 更新時間:2021年09月23日 09:03:16   作者:holly_wang_王小飛  
這篇文章主要介紹了java 實現日志追蹤MDC方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

java 日志追蹤MDC

MDC ( Mapped Diagnostic Contexts ) 有了日志之后,我們就可以追蹤各種線上問題。

但是,在分布式系統中,各種無關日志穿行其中,導致我們可能無法直接定位整個操作流程。

因此,我們可能需要對一個用戶的操作流程進行歸類標記,比如使用線程+時間戳,或者用戶身份標識等;如此,我們可以從大量日志信息中grep出某個用戶的操作流程,或者某個時間的流轉記錄。其目的是為了便于我們診斷線上問題而出現的方法工具類。

雖然,Slf4j 是用來適配其他的日志具體實現包的,但是針對 MDC功能,目前只有l(wèi)ogback 以及 log4j 支持。 MDC

package org.slf4j; 
import java.io.Closeable;
import java.util.Map; 
import org.slf4j.helpers.NOPMDCAdapter;
import org.slf4j.helpers.BasicMDCAdapter;
import org.slf4j.helpers.Util;
import org.slf4j.impl.StaticMDCBinder;
import org.slf4j.spi.MDCAdapter; 
public class MDC { 
    static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
    static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
    static MDCAdapter mdcAdapter; 
 
    public static class MDCCloseable implements Closeable {
        private final String key; 
        private MDCCloseable(String key) {
            this.key = key;
        } 
        public void close() {
            MDC.remove(this.key);
        }
    } 
    private MDC() {
    }
 
    static {
        try {
            mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA();
        } catch (NoClassDefFoundError ncde) {
            mdcAdapter = new NOPMDCAdapter();
            String msg = ncde.getMessage();
            if (msg != null && msg.indexOf("StaticMDCBinder") != -1) {
                Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
                Util.report("Defaulting to no-operation MDCAdapter implementation.");
                Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");
            } else {
                throw ncde;
            }
        } catch (Exception e) {
            // we should never get here
            Util.report("MDC binding unsuccessful.", e);
        }
    }
 
    public static void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key parameter cannot be null");
        }
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        mdcAdapter.put(key, val);
    }
 
    public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException {
        put(key, val);
        return new MDCCloseable(key);
    }
 
    public static String get(String key) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key parameter cannot be null");
        }
 
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        return mdcAdapter.get(key);
    }
 
    public static void remove(String key) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key parameter cannot be null");
        }
 
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        mdcAdapter.remove(key);
    } 
 
    public static void clear() {
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        mdcAdapter.clear();
    }
 
    public static Map<String, String> getCopyOfContextMap() {
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        return mdcAdapter.getCopyOfContextMap();
    } 
 
    public static void setContextMap(Map<String, String> contextMap) {
        if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
        }
        mdcAdapter.setContextMap(contextMap);
    } 
    public static MDCAdapter getMDCAdapter() {
        return mdcAdapter;
    } 
}

簡單的demo

package com.alibaba.otter.canal.common; 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC; 
public class LogTest {
    private static final Logger logger = LoggerFactory.getLogger(LogTest.class);
    public static void main(String[] args) {
        MDC.put("THREAD_ID", String.valueOf(Thread.currentThread().getId()));
        logger.info("純字符串信息的info級別日志");
    }
}

logback.xml 配置

<configuration scan="true" scanPeriod=" 5 seconds">
 
    <jmxConfigurator />
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} %X{THREAD_ID} - %msg%n
            </pattern>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration

對應的輸出日志 可以看到輸出了THREAD_ID

2016-12-08 14:59:32.855 [main] INFO com.alibaba.otter.canal.common.LogTest THREAD_ID 1 - 純字符串信息的info級別日志

slf4j只是起到適配的作用 故查看實現類LogbackMDCAdapter屬性

final InheritableThreadLocal<Map<String, String>> copyOnInheritThreadLocal = new InheritableThreadLocal<Map<String, String>>();

InheritableThreadLocal 該類擴展了 ThreadLocal,為子線程提供從父線程那里繼承的值:在創(chuàng)建子線程時,子線程會接收所有

可繼承的線程局部變量的初始值,以獲得父線程所具有的值。通常,子線程的值與父線程的值是一致的;但是,通過重寫這個類中的 childValue 方法,子線程的值可以作為父線程值的一個任意函數。

當必須將變量(如用戶 ID 和 事務 ID)中維護的每線程屬性(per-thread-attribute)自動傳送給創(chuàng)建的所有子線程時,應盡可能地采用可繼承的線程局部變量,而不是采用普通的線程局部變量

驗證一下

package com.alibaba.otter.canal.parse.driver.mysql; 
import org.junit.Test; 
public class TestInheritableThreadLocal {
    @Test
    public void testThreadLocal() {
        final ThreadLocal<String> local = new ThreadLocal<String>();  
        work(local);  
    }
 
    @Test
    public void testInheritableThreadLocal() {
        final ThreadLocal<String> local = new InheritableThreadLocal<String>();  
        work(local);
    }
    private void work(final ThreadLocal<String> local) {  
        local.set("a");  
        System.out.println(Thread.currentThread() + "," + local.get());  
        Thread t = new Thread(new Runnable() {  
              
            @Override  
            public void run() {  
                System.out.println(Thread.currentThread() + "," + local.get());  
                local.set("b");  
                System.out.println(Thread.currentThread() + "," + local.get());  
            }  
        });  
          
        t.start();  
        try {  
            t.join();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }            
        System.out.println(Thread.currentThread() + "," + local.get());  
    }  
}
 

分別運行得到的輸出結果

ThreadLocal 存貯輸出結果
Thread[main,5,main],a
Thread[Thread-0,5,main],null
Thread[Thread-0,5,main],b
Thread[main,5,main],a
InheritableThreadLocal存貯輸出結果
Thread[main,5,main],a
Thread[Thread-0,5,main],a
Thread[Thread-0,5,main],b
Thread[main,5,main],a

輸出結果說明一切 對于參數傳遞十分有用 我知道 canal的源碼中用到了MDC

在 CanalServerWithEmbedded 中的 start 和stop等方法中都有用到

    public void start(final String destination) {
        final CanalInstance canalInstance = canalInstances.get(destination);
        if (!canalInstance.isStart()) {
            try {
                MDC.put("destination", destination);
                canalInstance.start();
                logger.info("start CanalInstances[{}] successfully", destination);
            } finally {
                MDC.remove("destination");
            }
        }
    }
 
    public void stop(String destination) {
        CanalInstance canalInstance = canalInstances.remove(destination);
        if (canalInstance != null) {
            if (canalInstance.isStart()) {
                try {
                    MDC.put("destination", destination);
                    canalInstance.stop();
                    logger.info("stop CanalInstances[{}] successfully", destination);
                } finally {
                    MDC.remove("destination");
                }
            }
        }
    }

MDC的介紹及使用

1、MDC是什么?

MDC是(Mapped Diagnostic Context,映射調試上下文)是 log4j 和 logback 支持的一種方便在多線程條件下記錄追蹤日志的功能。通常打印出的日志會有線程號等信息來標志當前日志屬于哪個線程,然而由于線程是可以重復使用的,所以并不能很清晰的確認一個請求的日志范圍。處理這種情況一般有兩種處理方式:

1)手動生成一個唯一序列號打印在日志中;

2)使用日志控件提供的MDC功能,生成一個唯一序列標記一個線程的日志;

兩種方法的區(qū)別在于:

方法一只能標記一條日志,線程內其他日志需要人肉去篩選;

方法二標記整個線程的所有日志,方便grep命令查詢;

對比可見,使用MDC功能更好。

2、MDC的原理

MDC 可以看成是一個與當前線程綁定的哈希表,可以往其中添加鍵值對。MDC 中包含的內容可以被同一線程中執(zhí)行的代碼所訪問。當前線程的子線程會繼承其父線程中的 MDC 的內容。當需要記錄日志時,只需要從 MDC 中獲取所需的信息即可。MDC 的內容則由程序在適當的時候保存進去。對于一個 Web 應用來說,通常是在請求被處理的最開始保存這些數據。

@RunWith(SpringRunner.class)
@SpringBootTest(classes=CreditAppApplication.class)
publicclassMDCTest{
 
@Test
publicvoidmdcTest1(){
MDC.put("first","thefirst1");
 
Loggerlogger=LoggerFactory.getLogger(MDCTest.class);
MDC.put("last","thelast1");
 
logger.info("checkenclosed.");
logger.debug("themostbeautifultwowordsinenglish.");
 
MDC.put("first","thefirst2");
MDC.put("last","thelast2");
 
logger.info("iamnotacrook.");
logger.info("AttributedtotheformerUSpresident.17Nov1973.");
}
}

logback的配置:

3、MDC的使用

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
publicclassGlobalLogTagConfigextendsOncePerRequestFilter{
privatestaticfinalStringGLOBAL_LOG_TAG="GLOG_TAG"; 
privatestaticStringgenerateSeqNo(){
returnUUID.randomUUID().toString().replace("-","").substring(0,12);
}
 
@Override
protectedvoiddoFilterInternal(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,FilterChainfilterChain)throwsServletException,IOException{
try{
StringseqNo;
if(httpServletRequest!=null){
seqNo=httpServletRequest.getHeader(GLOBAL_LOG_TAG);
 
if(StringUtils.isEmpty(seqNo)){
seqNo=generateSeqNo();
}
}else{
seqNo=generateSeqNo();
}
MDC.put(GLOBAL_LOG_TAG,seqNo);
filterChain.doFilter(httpServletRequest,httpServletResponse);
}finally{
MDC.remove(GLOBAL_LOG_TAG);
}
}
}

注意:

OncePerRequestFilter的作用是為了讓每個請求只經過這個過濾器一次(因為web container的不同,有些過濾器可能被多次執(zhí)行)

logback配置:

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 剖析Fork?join并發(fā)框架工作竊取算法

    剖析Fork?join并發(fā)框架工作竊取算法

    這篇文章主要為大家剖析介紹了Fork?join并發(fā)框架工作竊取算法的示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • java 結合jQuery實現跨域名獲取數據的方法

    java 結合jQuery實現跨域名獲取數據的方法

    下面小編就為大家?guī)硪黄猨ava 結合jQuery實現跨域名獲取數據的方法。小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題解決方案

    SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題解決方案

    這篇文章主要介紹了SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題,對SpringCloud Alibaba Feign請求頭問題感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • Java Http請求傳json數據亂碼問題的解決

    Java Http請求傳json數據亂碼問題的解決

    這篇文章主要介紹了Java Http請求傳json數據亂碼問題的解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • 淺析Java進制轉換、輸入、命名問題

    淺析Java進制轉換、輸入、命名問題

    這篇文章主要介紹了Java進制轉換、輸入、命名問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • Java幾個實例帶你進階升華下篇

    Java幾個實例帶你進階升華下篇

    與其明天開始,不如現在行動,本文為你帶來幾個Java書寫的實際案例,對鞏固編程的基礎能力很有幫助,快來一起往下看看吧
    2022-03-03
  • SpringBoot給類進行賦初值的四種方式

    SpringBoot給類進行賦初值的四種方式

    這篇文章主要介紹了springboot給類進行賦初值的四種方式,并通過代碼示例給大家介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-08-08
  • SpringBoot項目Maven下載依賴速度慢問題的解決方法

    SpringBoot項目Maven下載依賴速度慢問題的解決方法

    在使用Maven構建項目時,有時會遇到下載依賴包速度慢的問題,為了提高下載速度,我們可以將默認的倉庫地址替換為國內鏡像源,所以本文介紹了SpringBoot項目Maven下載依賴速度慢問題的解決方法,需要的朋友可以參考下
    2024-08-08
  • MyBatis中如何優(yōu)雅的使用枚舉詳解

    MyBatis中如何優(yōu)雅的使用枚舉詳解

    枚舉類型是我們在開發(fā)中經常遇到的一個類型,最近在學習MyBatis,但是發(fā)現網上沒有詳細介紹MyBatis如何使用枚舉的相關文章,索性就自己寫一篇,下面這篇文章主要給大家介紹了關于在MyBatis中如何優(yōu)雅的使用枚舉的相關資料,需要的朋友可以參考借鑒。
    2017-08-08
  • Hibernate中獲取Session的兩種方式代碼示例

    Hibernate中獲取Session的兩種方式代碼示例

    這篇文章主要介紹了Hibernate中獲取Session的兩種方式代碼示例,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12

最新評論

县级市| 岳西县| 开平市| 普定县| 西昌市| 长丰县| 奉化市| 临汾市| 贵定县| 婺源县| 湟源县| 藁城市| 政和县| 睢宁县| 体育| 汉源县| 平乡县| 淮北市| 翁牛特旗| 若羌县| 哈巴河县| 松桃| 东丰县| 玛纳斯县| 正安县| 英超| 海晏县| 铁力市| 长岭县| 宁陵县| 固镇县| 惠东县| 本溪市| 武陟县| 车险| 五寨县| 武定县| 内黄县| 固原市| 黄石市| 阿克|