成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

日志與追蹤的完美融合:OpenTelemetry MDC實(shí)踐指南

開發(fā) 前端
通常我們排查問題的方式是先查詢異常日志,判斷是否是當(dāng)前系統(tǒng)的問題。如果不是,則在日志中撈出 trace_id 再到鏈路查詢系統(tǒng)中查詢鏈路,看看具體是哪個(gè)系統(tǒng)的問題,然后再做具體的排查。

前言

通常我們排查問題的方式是先查詢異常日志,判斷是否是當(dāng)前系統(tǒng)的問題。

如果不是,則在日志中撈出 trace_id 再到鏈路查詢系統(tǒng)中查詢鏈路,看看具體是哪個(gè)系統(tǒng)的問題,然后再做具體的排查。

類似于這樣:

圖片圖片

日志中會(huì)打印 trace_id 和 span_id。

如果日志系統(tǒng)做的比較完善的話,還可以直接點(diǎn)擊 trace_id 跳轉(zhuǎn)到鏈路系統(tǒng)里直接查詢鏈路信息。

MDC

這里的日志里關(guān)聯(lián) trace 信息的做法有個(gè)專有名詞:MDC:(Mapped Diagnostic Context)。

簡(jiǎn)單來說就是用于排查問題的上下文信息,通常是由鍵值對(duì)組成,類似于這樣的數(shù)據(jù):

{  
  "timestamp" : "2024-08-05 17:27:31.097",  
  "level" : "INFO",  
  "thread" : "http-nio-9191-exec-1",  
  "mdc" : {  
    "trace_id" : "26242f945af80b044a60226af00211fb",  
    "trace_flags" : "01",  
    "span_id" : "3a7842b3e28ed5c8"  
  },  
  "logger" : "com.example.demo.DemoApplication",  
  "message" : "request: name: \"1232\"\n",  
  "context" : "default"  
}

在 Java 中的 Log4j 和 Logback 都有提供對(duì)應(yīng)的實(shí)現(xiàn)。

如果我們使用了 OpenTelemetry 提供的 javaagent 再配合 logback 或者 Log4j 時(shí)就會(huì)自動(dòng)具備打印 MDC 的能力:

java -javaagent:/Users/chenjie/Downloads/blog-img/demo/opentelemetry-javaagent-2.4.0-SNAPSHOT.jar xx.jar

比如我們只需要這樣配置這樣一個(gè)JSON 輸出的 logback 即可:

<appender name="PROJECT_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">  
    <file>${PATH}/demo.log</file>  
  
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">  
        <fileNamePattern>${PATH}/demo_%i.log</fileNamePattern>  
        <maxIndex>1</maxIndex>  
    </rollingPolicy>  
  
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">  
        <maxFileSize>100MB</maxFileSize>  
    </triggeringPolicy>  
  
    <layout class="ch.qos.logback.contrib.json.classic.JsonLayout">  
        <jsonFormatter  
                class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">  
            <prettyPrint>true</prettyPrint>  
        </jsonFormatter>  
        <timestampFormat>yyyy-MM-dd' 'HH:mm:ss.SSS</timestampFormat>  
    </layout>  
  
</appender>  
  
<root level="INFO">  
    <appender-ref ref="STDOUT"/>  
    <appender-ref ref="PROJECT_LOG"/>  
</root>

圖片圖片

就會(huì)在日志文件中輸出 JSON 格式的日志,并且?guī)?MDC 的信息。

自動(dòng) MDC 的原理

我也比較好奇 OpenTelemetry 是如何自動(dòng)寫入 MDC 信息的,這里以 logback 為例。

@Override  
public ElementMatcher<TypeDescription> typeMatcher() {  
  return implementsInterface(named("ch.qos.logback.classic.spi.ILoggingEvent"));  
}  
  
@Override  
public void transform(TypeTransformer transformer) {  
  transformer.applyAdviceToMethod(  
      isMethod()  
          .and(isPublic())  
          .and(namedOneOf("getMDCPropertyMap", "getMdc"))  
          .and(takesArguments(0)),  
      LoggingEventInstrumentation.class.getName() + "$GetMdcAdvice");  
}

會(huì)在調(diào)用 ch.qos.logback.classic.spi.ILoggingEvent.getMDCPropertyMap()/getMdc() 這兩個(gè)函數(shù)中進(jìn)行埋點(diǎn)。

這些邏輯都是寫在 javaagent 中的。

這個(gè)函數(shù)其實(shí)默認(rèn)情況下會(huì)返回一個(gè) logback 內(nèi)置 MDC 的 map 數(shù)據(jù)(這里的數(shù)據(jù)我們可以自定義配置)。

而這里要做的就是將 trace 的上下文信息寫入這個(gè) mdcPropertyMap 中。

以下是 OpenTelemetry agent 中的源碼:

Map<String, String> spanContextData = new HashMap<>();  
  
SpanContext spanContext = Java8BytecodeBridge.spanFromContext(context).getSpanContext();  
  
if (spanContext.isValid()) {  
  spanContextData.put(traceIdKey(), spanContext.getTraceId());  
  spanContextData.put(spanIdKey(), spanContext.getSpanId());  
  spanContextData.put(traceFlagsKey(), spanContext.getTraceFlags().asHex());  
}  
spanContextData.putAll(ConfiguredResourceAttributesHolder.getResourceAttributes());  
  
if (LogbackSingletons.addBaggage()) {  
  Baggage baggage = Java8BytecodeBridge.baggageFromContext(context);  
  
  // using a lambda here does not play nicely with instrumentation bytecode process  
  // (Java 6 related errors are observed) so relying on for loop instead  for (Map.Entry<String, BaggageEntry> entry : baggage.asMap().entrySet()) {  
    spanContextData.put(  
        // prefix all baggage values to avoid clashes with existing context  
        "baggage." + entry.getKey(), entry.getValue().getValue());  
  }}  
  
if (contextData == null) {  
  contextData = spanContextData;  
} else {  
  contextData = new UnionMap<>(contextData, spanContextData);  
}

這就是核心的寫入邏輯,從這個(gè)代碼中也可以看出直接從上線文中獲取的 span 的 context,而我們所需要的 trace_id/span_id  都是存放在 context 中的,只需要 get 出來然后寫入進(jìn) map 中即可。

從源碼里還得知,只要我們開啟 -Dotel.instrumentation.logback-mdc.add-baggage=true 配置還可以將 baggage 中的數(shù)據(jù)也寫入到 MDC 中。

而得易于 OpenTelemetry 中的 trace 是可以跨線程傳輸?shù)模约幢闶俏覀冊(cè)诙嗑€程里打印日志時(shí) MDC 數(shù)據(jù)依然可以準(zhǔn)確無誤的傳遞。

MDC 的原理

public static final String MDC_ATTR_NAME = "mdc";

圖片圖片

在 logback 的實(shí)現(xiàn)中是會(huì)調(diào)用剛才的 getMDCPropertyMap() 然后寫入到一個(gè) key 為 mdc 的 map 里,最終可以寫入到文件或者控制臺(tái)。

這樣整個(gè)原理就可以串起來了。

自定義日志 數(shù)據(jù)

提到可以自定義 MDC 數(shù)據(jù)其實(shí)也是有使用場(chǎng)景的,比如我們的業(yè)務(wù)系統(tǒng)經(jīng)常有類似的需求,需要在日志中打印一些常用業(yè)務(wù)數(shù)據(jù):

  • userId、userName
  • 客戶端 IP等信息時(shí)

此時(shí)我們就可以創(chuàng)建一個(gè) Layout 類來繼承 ch.qos.logback.contrib.json.classic.JsonLayout:

public class CustomJsonLayout extends JsonLayout {
    public CustomJsonLayout() {
    }

    protected void addCustomDataToJsonMap(Map<String, Object> map, ILoggingEvent event) {
        map.put("user_name", context.getProperty("userName"));
        map.put("user_id", context.getProperty("userId"));
        map.put("trace_id", TraceContext.traceId());
    }
}


public class CustomJsonLayoutEncoder extends LayoutWrappingEncoder<ILoggingEvent> {  
    public CustomJsonLayoutEncoder() {  
    }  
    public void start() {  
        CustomJsonLayout jsonLayout = new CustomJsonLayout();  
        jsonLayout.setContext(this.context);  
        jsonLayout.setIncludeContextName(false);  
        jsonLayout.setAppendLineSeparator(true);  
        jsonLayout.setJsonFormatter(new JacksonJsonFormatter());  
        jsonLayout.start();  
        super.setCharset(StandardCharsets.UTF_8);  
        super.setLayout(jsonLayout);  
        super.start();  
    }}

這里的 trace_id 是之前使用 skywalking 的時(shí)候由 skywalking 提供的函數(shù):org.apache.skywalking.apm.toolkit.trace.TraceContext#traceId

接著只需要在 logback.xml 中配置這個(gè) CustomJsonLayoutEncoder 就可以按照我們自定義的數(shù)據(jù)輸出日志了:

<appender name="PROJECT_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">  
    <file>${PATH}/app.log</file>  
  
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">  
        <fileNamePattern>${PATH}/app_%i.log</fileNamePattern>  
        <maxIndex>1</maxIndex>  
    </rollingPolicy>  
  
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">  
        <maxFileSize>100MB</maxFileSize>  
    </triggeringPolicy>  
  
    <encoder class="xx.CustomJsonLayoutEncoder"/>  
</appender>

<root level="INFO">  
    <appender-ref ref="STDOUT"/>  
    <appender-ref ref="PROJECT_LOG"/>  
</root>

雖然這個(gè)功能也可以使用日志切面來打印,但還是沒有直接在日志中輸出更加方便,它可以直接和我們的日志關(guān)聯(lián)在一起,只是多加了這幾個(gè)字段而已。

Spring Boot 使用

OpenTelemetry 有給 springboot 應(yīng)用提供一個(gè) spring-boot-starter 包,用于在不使用  javaagent 的情況下也可以自動(dòng)埋點(diǎn)。

<dependencies>
  <dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
    <version>OPENTELEMETRY_VERSION</version>
  </dependency>
</dependencies>

但在早期的版本中還不支持直接打印 MDC 日志:

圖片圖片

最新的版本已經(jīng)支持

即便已經(jīng)支持默認(rèn)輸出 MDC 后,我們依然可以自定義的內(nèi)容,比如我們想修改一下 key 的名稱,由 trace_id 修改為 otel_trace_id 等。

<appender name="OTEL" class="io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender">
  <traceIdKey>otel_trace_id</traceIdKey>
  <spanIdKey>otel_span_id</spanIdKey>
  <traceFlagsKey>otel_trace_flags</traceFlagsKey>
</appender>

還是和之前類似,修改下 logback.xml 即可。

他的實(shí)現(xiàn)邏輯其實(shí)和之前的 auto instrument 中的類似,只不過使用的 API 不同而已。

auto instrument 是直接攔截代碼邏輯修改 map 的返回值,而 OpenTelemetryAppender 是繼承了 ch.qos.logback.core.UnsynchronizedAppenderBase 接口,從而獲得了重寫 MDC 的能力,但本質(zhì)上都是一樣的,沒有太大區(qū)別。

不過使用它的前提是我們需要引入以下一個(gè)依賴:

<dependencies>
  <dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-logback-mdc-1.0</artifactId>
    <version>OPENTELEMETRY_VERSION</version>
  </dependency>
</dependencies>

如果不想修改 logback.yaml ,對(duì)于 springboot 來說還有更簡(jiǎn)單的方案,我們只需要使用以下配置即可自定義 MDC 數(shù)據(jù):

logging.pattern.level = trace_id=%mdc{trace_id} span_id=%mdc{span_id} trace_flags=%mdc{trace_flags} %5p

這里的 key 也可以自定義,只要占位符沒有取錯(cuò)即可。

使用這個(gè)的前提是需要加載  javaagent,因?yàn)檫@里的數(shù)據(jù)是 javaagent 里寫進(jìn)去的。

總結(jié)

以上就是關(guān)于 MDC 在 OpenTelemetry 中的使用,從使用和源碼邏輯上都分析了一遍,希望對(duì) MDC 和 OpenTelemetry 的理解更加深刻一些。

關(guān)于 MDC 相關(guān)的概念與使用還是很有用的,是日常排查問題必不可少的一個(gè)工具。

責(zé)任編輯:武曉燕 來源: crossoverJie
相關(guān)推薦

2024-05-21 08:09:00

OpenTelemetry倉庫

2023-10-16 23:43:52

云原生可觀測(cè)性

2024-06-14 08:19:45

2024-06-27 08:41:21

2012-10-19 12:49:40

NAS系統(tǒng)性能N8500OPS

2015-11-04 15:13:56

華為

2023-02-06 09:36:00

騰訊燈塔融合引擎

2013-01-14 11:37:29

惠普電腦

2024-06-07 07:41:03

2023-11-23 10:45:13

Next.js 14Supabase

2009-08-31 17:52:12

NehalemSSDSolaris

2025-01-20 08:10:00

微服務(wù)架構(gòu)SLF4J

2024-02-01 08:00:00

百川大模型角色大模型

2025-05-26 08:50:00

SLF4JMDC全鏈路追蹤

2012-04-26 19:46:06

反釣魚沙龍

2019-08-01 10:57:52

開發(fā)者技能TypeScript

2012-10-29 16:22:18

遨游瀏覽器

2022-07-05 10:38:23

BGPCalicoMetalLB

2023-12-06 07:24:42

屬性命名云原生
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产精品久久久久久久久久久免费看 | 国产精品视频一二三区 | 成人精品在线视频 | 一区二区三区四区av | 人人九九精 | 亚洲精品在线播放 | 亚洲国产一区二区三区在线观看 | 日本亚洲一区 | 成人精品视频在线观看 | 一区二区三区高清在线观看 | 国产精品伦一区二区三级视频 | 久久精品小短片 | 国产激情网| 日韩黄色av | 欧美日韩视频一区二区 | 国产精品久久久久国产a级 欧美日本韩国一区二区 | 国产精品91视频 | 国产亚洲成av人片在线观看桃 | 亚洲国产专区 | 亚洲高清中文字幕 | 日日想夜夜操 | 亚洲精品久久久久久久久久久久久 | 国产精品久久久久久久久免费桃花 | 色噜噜色综合 | 日韩视频中文字幕 | 亚洲一区在线播放 | 中日字幕大片在线播放 | 男女羞羞在线观看 | 精品欧美一区二区久久久伦 | 国产视频91在线 | 午夜免费网站 | 日韩一区二区三区av | 精品无码久久久久久国产 | 日韩在线综合网 | 亚洲欧美国产精品久久 | 国产视频中文字幕 | 欧美黑人国产人伦爽爽爽 | 国产xxxx在线 | 国产激情一区二区三区 | 日本一道本视频 | 九九色综合 |