MDC skywalking與ThreadLocal

MDC

A Mapped Diagnostic Context, or MDC in short, is an instrument for distinguishing interleaved log output from different sources. Log output is typically interleaved when a server handles multiple clients near-simultaneously.

The MDC is managed on a per thread basis. Note that a child thread does not inherit the mapped diagnostic context of its parent.

映射診斷上下文萝衩,簡(jiǎn)稱MDC尊沸,是一種用于區(qū)分來自不同來源的交錯(cuò)日志輸出的工具。當(dāng)服務(wù)器幾乎同時(shí)處理多個(gè)客戶端時(shí)仪壮,日志輸出通常是交錯(cuò)的喜滨。

MDC基于每個(gè)線程進(jìn)行管理。請(qǐng)注意,子線程不繼承其父線程的MDC。

以上解釋來自LogbackMDCAdapter(pom定義如下)

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.7</version>
</dependency>

ThreadLocal的功能是為當(dāng)前線程維護(hù)一個(gè)map

org.slf4j.MDC基于ThreadLocal忆肾,集成了日志組件,具體實(shí)現(xiàn)有Log4jMDCAdapter和LogbackMDCAdapter

org.slf4j.MDC的ThreadLocal里的key菱肖,可以在log4j/logback.xml中定義占位符,并輸出到日志

比如旭从,執(zhí)行MDC.put("traceId", traceId)稳强,logback.xml中定義如下,最后輸出的日志和悦,就能打印出traceId

<!--格式化輸出:%d表示日期退疫,%thread表示線程名,%traceId表示分布式鏈路id鸽素,%-5level:級(jí)別從左顯示5個(gè)字符寬度褒繁,%msg表示日志消息,%n表示換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%traceId] %-5level - %msg%n</pattern>

至于MDC中的ThreadLocal馍忽,有沒有跨線程復(fù)制的能力(父線程的上下文復(fù)制到子線程)棒坏,與MDCAdapter的版本有關(guān)。

在1.4.7版本的LogbackMDCAdapter遭笋,注釋寫的是Note that a child thread does not inherit the mapped diagnostic context of its parent. 子線程不繼承父線程的MDC坝冕。

在1.2.9版本的LogbackMDCAdapter,注釋寫的是A child thread automatically inherits a copy of the mapped diagnostic context of its parent. 子線程自動(dòng)繼承父線程的MDC瓦呼。

Java Instrument && Java Agent

Java Instrument

Java Instrument是JDK1.5提供的一個(gè)新特性喂窟,用于監(jiān)測(cè)JVM進(jìn)程,甚至可以修改類的實(shí)現(xiàn)央串。有了這樣的功能磨澡,就可以更靈活的實(shí)現(xiàn)運(yùn)行時(shí)虛擬機(jī)監(jiān)控和Java類操作,這樣的特性可以看做是一種虛擬機(jī)級(jí)別的AOP實(shí)現(xiàn)方式质和。

image.png

ClassFileTransformer是類文件的轉(zhuǎn)換器

Instrumentation提供監(jiān)測(cè)Java程序所需的服務(wù)

Java Agent

Java Agent是一種特殊的Java程序稳摄,可以看做是Java Instrumentation的客戶端。普通Java程序通過main函數(shù)啟動(dòng)侦另,Java Agent無法單獨(dú)啟動(dòng)秩命,必須依附于一個(gè)Java程序上,與他運(yùn)行在同一個(gè)進(jìn)程中褒傅,通過Java Instrumentation的客戶端 API與虛擬機(jī)交互弃锐。

JVM會(huì)把Instrumentation的實(shí)例作為參數(shù)注入到Java Agent的啟動(dòng)方法上,因此要使用Instrumentation功能殿托,必須通過Java Agent霹菊。

Java Agent有2個(gè)加載時(shí)機(jī),一個(gè)是JVM啟動(dòng)前通過-javaagent參數(shù)靜態(tài)加載執(zhí)行,另一個(gè)是JVM啟動(dòng)后通過Java Tool API中的attach api動(dòng)態(tài)加載執(zhí)行旋廷。

skywalking

skywalking的主要作用是以javaAgent的形式(也可以有別的形式)鸠按,收集應(yīng)用程序的鏈路信息(請(qǐng)求入?yún)ⅰ㈨憫?yīng)結(jié)果饶碘、耗時(shí)等等)目尖,上傳到存儲(chǔ)組件,并提供可視化展示扎运。

源碼倉庫: https://github.com/apache/skywalking.git

分支: 6.x

Trace注解

方法上添加了@Trace注解瑟曲,會(huì)執(zhí)行TraceAnnotationMethodInterceptor攔截器,攔截器中會(huì)創(chuàng)建span

package org.apache.skywalking.apm.toolkit.activation.trace;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.DeclaredInstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import org.apache.skywalking.apm.agent.core.plugin.match.MethodAnnotationMatch;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;

import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.named;

/**
 * {@link TraceAnnotationActivation} enhance all method that annotated with <code>org.apache.skywalking.apm.toolkit.trace.annotation.Trace</code>
 * by <code>TraceAnnotationMethodInterceptor</code>.
 *
 * @author zhangxin
 */
public class TraceAnnotationActivation extends ClassInstanceMethodsEnhancePluginDefine {

    public static final String TRACE_ANNOTATION_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.toolkit.activation.trace.TraceAnnotationMethodInterceptor";
    public static final String TRACE_ANNOTATION = "org.apache.skywalking.apm.toolkit.trace.Trace";

    @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new DeclaredInstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return isAnnotatedWith(named(TRACE_ANNOTATION));
                }

                @Override public String getMethodsInterceptor() {
                    return TRACE_ANNOTATION_METHOD_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override protected ClassMatch enhanceClass() {
        return MethodAnnotationMatch.byMethodAnnotationMatch(new String[] {TRACE_ANNOTATION});
    }
}

TraceCrossThread注解

類上添加了@TraceCrossThread注解豪治,并且存在名為call, run, get的方法洞拨,會(huì)執(zhí)行CallableOrRunnableInvokeInterceptor攔截器,攔截器中會(huì)創(chuàng)建span

package org.apache.skywalking.apm.toolkit.activation.trace;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;

import static net.bytebuddy.matcher.ElementMatchers.*;
import static org.apache.skywalking.apm.agent.core.plugin.match.ClassAnnotationMatch.byClassAnnotationMatch;

/**
 * {@link CallableOrRunnableActivation} presents that skywalking intercepts all Class with annotation
 * "org.skywalking.apm.toolkit.trace.TraceCrossThread" and method named "call" or "run".
 *
 * @author carlvine500 lican
 */
public class CallableOrRunnableActivation extends ClassInstanceMethodsEnhancePluginDefine {

    public static final String ANNOTATION_NAME = "org.apache.skywalking.apm.toolkit.trace.TraceCrossThread";
    private static final String INIT_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.toolkit.activation.trace.CallableOrRunnableConstructInterceptor";
    private static final String CALL_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.toolkit.activation.trace.CallableOrRunnableInvokeInterceptor";
    private static final String CALL_METHOD_NAME = "call";
    private static final String RUN_METHOD_NAME = "run";
    private static final String GET_METHOD_NAME = "get";

    @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[] {
            new ConstructorInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return any();
                }

                @Override public String getConstructorInterceptor() {
                    return INIT_METHOD_INTERCEPTOR;
                }
            }
        };
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return (named(CALL_METHOD_NAME).and(takesArguments(0)))
                        .or(named(RUN_METHOD_NAME).and(takesArguments(0)))
                        .or(named(GET_METHOD_NAME).and(takesArguments(0)));
                }

                @Override public String getMethodsInterceptor() {
                    return CALL_METHOD_INTERCEPTOR;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override protected ClassMatch enhanceClass() {
        return byClassAnnotationMatch(new String[] {ANNOTATION_NAME});
    }

}

ThreadLocal

java.lang.ThreadLocal無法復(fù)制上下文至子線程

java.lang.InheritableThreadLocal只有在線程創(chuàng)建時(shí)负拟,把父線程的上下文復(fù)制到子線程

阿里開源的com.alibaba.ttl.TransmittableThreadLocal烦衣,可以在子線程執(zhí)行任務(wù)前,把父線程的上下文復(fù)制到子線程
pom

<!-- https://mvnrepository.com/artifact/com.alibaba/transmittable-thread-local -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.12.0</version>
</dependency>

跨進(jìn)程傳遞上下文

TransmittableThreadLocal只能解決同進(jìn)程內(nèi)跨線程復(fù)制的問題掩浙,如果需要跨進(jìn)程復(fù)制上下文花吟,需要有攔截器。

以分布式鏈路id traceId的實(shí)現(xiàn)為例涣脚,

比如A服務(wù)http請(qǐng)求B服務(wù)示辈,可能會(huì)通過Feign, RestTemplate, HttpClient等形式。需要為項(xiàng)目中用到的每個(gè)形式遣蚀,定義攔截器矾麻,發(fā)送http請(qǐng)求時(shí),從MDC取出traceId芭梯,放到http header里险耀,key為trace-id。

B服務(wù)也需要有一個(gè)攔截器玖喘,從http header里拿出trace-id甩牺,放到MDC里。

如果是mq的場(chǎng)景累奈,也是一樣的

生產(chǎn)者需要從MDC取出traceId贬派,放到mq的properties里面

消費(fèi)者需要從mq的properties里,拿出traceId澎媒,放入MDC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搞乏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子戒努,更是在濱河造成了極大的恐慌请敦,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異侍筛,居然都是意外死亡萤皂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門匣椰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裆熙,“玉大人,你說我怎么就攤上這事禽笑〕诔担” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵蒲每,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我喻括,道長(zhǎng)邀杏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任唬血,我火速辦了婚禮望蜡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拷恨。我一直安慰自己脖律,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布腕侄。 她就那樣靜靜地躺著小泉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冕杠。 梳的紋絲不亂的頭發(fā)上微姊,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音分预,去河邊找鬼兢交。 笑死,一個(gè)胖子當(dāng)著我的面吹牛笼痹,可吹牛的內(nèi)容都是我干的配喳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼凳干,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼晴裹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纺座,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤息拜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體少欺,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喳瓣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赞别。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畏陕。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仿滔,靈堂內(nèi)的尸體忽然破棺而出惠毁,到底是詐尸還是另有隱情,我是刑警寧澤崎页,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布鞠绰,位于F島的核電站,受9級(jí)特大地震影響飒焦,放射性物質(zhì)發(fā)生泄漏蜈膨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一牺荠、第九天 我趴在偏房一處隱蔽的房頂上張望翁巍。 院中可真熱鬧,春花似錦休雌、人聲如沸灶壶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驰凛。三九已至,卻和暖如春担扑,著一層夾襖步出監(jiān)牢的瞬間洒嗤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工魁亦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渔隶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓洁奈,卻偏偏與公主長(zhǎng)得像间唉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子利术,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 大家好久不見呈野,我是問北。今天給大家?guī)硪粋€(gè)日志方面的知識(shí)——MDC印叁,不知道大家認(rèn)識(shí)不被冒,反正我是最近剛知道的 初見M...
    BiggerBoy閱讀 491評(píng)論 0 0
  • MDC 簡(jiǎn)介 MDC ( Mapped Diagnostic Contexts )军掂,它是一個(gè)線程安全的存放診斷日志...
    xiaolyuh閱讀 23,598評(píng)論 0 14
  • 前言 在做分布式鏈路追蹤系統(tǒng)的時(shí)候,需要解決異步調(diào)用透?jìng)魃舷挛牡男枨笞虻浚貏e是傳遞traceId蝗锥,本文就線程池透?jìng)鲙?..
    猿必過閱讀 1,065評(píng)論 0 0
  • 一、目的 開發(fā)排查系統(tǒng)問題用得最多的手段就是查看系統(tǒng)日志率触,但是在分布式環(huán)境下使用日志定位問題還是比較麻煩终议,需要借助...
    zlt2000閱讀 743評(píng)論 0 3
  • 一、項(xiàng)目背景 2017年葱蝗,vivo互聯(lián)網(wǎng)研發(fā)團(tuán)隊(duì)認(rèn)為調(diào)用鏈系統(tǒng)對(duì)實(shí)際業(yè)務(wù)具有較大的價(jià)值穴张,于是開始了研發(fā)工作。3年的...
    vivo互聯(lián)網(wǎng)技術(shù)閱讀 566評(píng)論 0 0