Skip to main content

Jaeger Trace MDC

Simple Demo Code

1. ๆ–นๆกˆๆฆ‚่ฟฐโ€‹

1.1 ่ƒŒๆ™ฏไธŽ็›ฎๆ ‡โ€‹

ๅœจๅˆ†ๅธƒๅผ็ณป็ปŸไธญ๏ผŒ่ทจๆœๅŠก็š„่ฏทๆฑ‚้“พ่ทฏ่ฟฝ่ธชๆ˜ฏๅฎšไฝ้—ฎ้ข˜็š„ๅ…ณ้”ฎๆ‰‹ๆฎตใ€‚ๆœฌๆ–นๆกˆๆ—จๅœจๅฎž็Žฐ Jaeger ๅˆ†ๅธƒๅผ่ฟฝ่ธชไธŽ SLF4J MDC๏ผˆMapped Diagnostic Context๏ผ‰็š„ๆทฑๅบฆ้›†ๆˆ๏ผŒไฝฟๅพ—๏ผš

  • ๆ—ฅๅฟ—่‡ชๅŠจๆบๅธฆ traceId/spanId๏ผŒไพฟไบŽๆ—ฅๅฟ—่šๅˆๆŸฅ่ฏข
  • ๆ”ฏๆŒ่ทจๆœๅŠก็š„ trace ไธŠไธ‹ๆ–‡ไผ ้€’
  • ๅœจ็บฟ็จ‹ๆฑ ็ญ‰ๅผ‚ๆญฅๅœบๆ™ฏไธ‹ไฟๆŒ trace ไธŠไธ‹ๆ–‡็š„ๅ‡†็กฎๆ€ง

1.2 ๆ ธๅฟƒไปทๅ€ผโ€‹

  • ้—ฎ้ข˜ๅฎšไฝๆ•ˆ็އๆๅ‡๏ผš้€š่ฟ‡ traceId ๅฟซ้€Ÿๅ…ณ่”ๅˆ†ๅธƒๅผ็ณป็ปŸไธญ็š„ๆ‰€ๆœ‰็›ธๅ…ณๆ—ฅๅฟ—
  • ้›ถไพตๅ…ฅๆ€ง๏ผšไธšๅŠกไปฃ็ ๆ— ้œ€ๆ‰‹ๅŠจ็ฎก็† MDC๏ผŒ่‡ชๅŠจๆณจๅ…ฅๅ’Œๆธ…็†
  • ็บฟ็จ‹ๅฎ‰ๅ…จ๏ผšๆ”ฏๆŒๅตŒๅฅ— Span ๅ’Œ็บฟ็จ‹ๆฑ ๅค็”จๅœบๆ™ฏ

2. ๆžถๆž„่ฎพ่ฎกโ€‹

2.1 ๆ•ดไฝ“ๆžถๆž„ๅ›พโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Application Layer โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Controller โ”‚โ”€โ”€โ”€>โ”‚ Service โ”‚โ”€โ”€โ”€>โ”‚ DAO โ”‚ โ”‚
โ”‚ โ”‚ (HTTP Entry) โ”‚ โ”‚ (Business) โ”‚ โ”‚ (Database) โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Tracing Layer โ–ผ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ CustomMDCScopeManager (ๆ ธๅฟƒ็ป„ไปถ) โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ ThreadLocal<CustomMDCScope> โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ - ็ฎก็† Scope ็”Ÿๅ‘ฝๅ‘จๆœŸ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ - ็ปดๆŠค Span ๆ ˆ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ CustomMDCScope (Scope ๅฎž็Žฐ) โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ - MDC ๅฟซ็…งไธŽๆขๅค โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ - ๆ”ฏๆŒๅตŒๅฅ— Span โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Logging Layer โ–ผ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ SLF4J MDC โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ traceId: 04bf92f3577b34da... โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ spanId: 36bd32b7a5712a1a โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ parentId: 00f067aa0ba902b7 โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Jaeger Collector โ”‚
โ”‚ (Trace Storage) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2.2 ๆ ธๅฟƒ็ป„ไปถ่ฏดๆ˜Žโ€‹

2.2.1 CustomMDCScopeManagerโ€‹

่Œ่ดฃ๏ผšๅฎž็Žฐ OpenTracing ็š„ ScopeManager ๆŽฅๅฃ๏ผŒ็ฎก็† Span ็š„ๆฟ€ๆดปไธŽไผ ๆ’ญ

ๅ…ณ้”ฎๅฎž็Žฐ๏ผš

private final ThreadLocal<CustomMDCScope> tlsScope = new ThreadLocal<>();

@Override
public Scope activate(Span span) {
return new CustomMDCScope(span);
}

@Override
public Span activeSpan() {
CustomMDCScope scope = tlsScope.get();
return scope == null ? null : scope.wrapped;
}

่ฎพ่ฎก่ฆ็‚น๏ผš

  • ไฝฟ็”จ ThreadLocal ไฟ่ฏ็บฟ็จ‹้š”็ฆป
  • ๆ”ฏๆŒ Scope ็š„ๅตŒๅฅ—๏ผˆ้€š่ฟ‡้“พ่กจ็ป“ๆž„็ปดๆŠค previous๏ผ‰
  • ๅฎž็Žฐๆ‡’ๆฟ€ๆดป๏ผšไป…ๅœจ่ฐƒ็”จ activate() ๆ—ถๆ‰ๆณจๅ…ฅ MDC

2.2.2 CustomMDCScopeโ€‹

่Œ่ดฃ๏ผšๅฎž็Žฐ OpenTracing ็š„ Scope ๆŽฅๅฃ๏ผŒ็ฎก็†ๅ•ไธช Span ็š„็”Ÿๅ‘ฝๅ‘จๆœŸ

ๆ ธๅฟƒๆœบๅˆถ๏ผšๅฟซ็…ง-ๆณจๅ…ฅ-ๆขๅค๏ผˆSnapshot-Inject-Restore๏ผ‰

CustomMDCScope(Span span) {
// 1. ไฟๅญ˜ๅฝ“ๅ‰ MDC ๅฟซ็…ง
this.previousTraceId = MDC.get("traceId");
this.previousSpanId = MDC.get("spanId");

// 2. ๅปบ็ซ‹้“พ่กจๅ…ณ็ณป๏ผˆๆ”ฏๆŒๅตŒๅฅ—๏ผ‰
this.previous = CustomMDCScopeManager.this.tlsScope.get();
CustomMDCScopeManager.this.tlsScope.set(this);

// 3. ๆณจๅ…ฅๆ–ฐ็š„ trace ไธŠไธ‹ๆ–‡
MDC.put("traceId", span.context().toTraceId());
MDC.put("spanId", span.context().toSpanId());
}

@Override
public void close() {
// 4. ๆขๅคๅˆฐไธŠไธ€ๅฑ‚ Scope
CustomMDCScopeManager.this.tlsScope.set(previous);

// 5. ๆขๅค MDC ๅˆฐๅฟซ็…ง็Šถๆ€
restoreMDC("traceId", previousTraceId);
restoreMDC("spanId", previousSpanId);
}

3. ๅ…ณ้”ฎๆต็จ‹่ฎพ่ฎกโ€‹

3.1 ๆŽฅๆ”ถ่ฟœ็จ‹ Trace ไธŠไธ‹ๆ–‡็š„ๆต็จ‹โ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ HTTP Requestโ”‚
โ”‚ Headers โ”‚
โ”‚ uber-trace-id: 4bf92f3577b...โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. ่งฃๆž HTTP Header โ”‚
โ”‚ - ๆๅ– traceId (128-bit) โ”‚
โ”‚ - ๆๅ– parentSpanId (64-bit) โ”‚
โ”‚ - ๆๅ– flags (้‡‡ๆ ทๆ ‡่ฎฐ) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 2. ๆž„้€  JaegerSpanContext โ”‚
โ”‚ long[] parts = splitTraceId() โ”‚
โ”‚ new JaegerSpanContext( โ”‚
โ”‚ traceIdHigh, โ”‚
โ”‚ traceIdLow, โ”‚
โ”‚ parentSpanId, โ”‚
โ”‚ parentOfParentId, โ”‚
โ”‚ flags โ”‚
โ”‚ ) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 3. ๅˆ›ๅปบๅญ Span โ”‚
โ”‚ tracer.buildSpan("child-span")โ”‚
โ”‚ .asChildOf(parentContext)โ”‚
โ”‚ .withTag(...) โ”‚
โ”‚ .start() โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 4. ๆฟ€ๆดป Span ๅˆฐๅฝ“ๅ‰็บฟ็จ‹ โ”‚
โ”‚ try (Scope scope = โ”‚
โ”‚ tracer.scopeManager() โ”‚
โ”‚ .activate(childSpan)) โ”‚
โ”‚ { โ”‚
โ”‚ // MDC ่‡ชๅŠจๆณจๅ…ฅ โ”‚
โ”‚ // ไธšๅŠก้€ป่พ‘ๆ‰ง่กŒ โ”‚
โ”‚ } // MDC ่‡ชๅŠจๆธ…็† โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3.2 ๅตŒๅฅ— Span ็š„ MDC ็ฎก็†ๆต็จ‹โ€‹

ๆ—ถ้—ด็บฟ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>

ThreadLocal Stack:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ null โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

activate(spanA)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ScopeA: {traceId: xxx, spanId: A, previous: null}โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
MDC: {traceId: xxx, spanId: A}

activate(spanB) // ๅตŒๅฅ—่ฐƒ็”จ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ScopeB: {traceId: xxx, spanId: B, previous: ScopeA}โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
MDC: {traceId: xxx, spanId: B} // spanId ๆ›ดๆ–ฐ

// ไธšๅŠกไปฃ็ ๆ‰ง่กŒ
log.info("Processing...") // ๆ—ฅๅฟ—ๆบๅธฆ spanId=B

close(ScopeB)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ScopeA: {traceId: xxx, spanId: A, previous: null}โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
MDC: {traceId: xxx, spanId: A} // ๆขๅคๅˆฐ spanId=A

close(ScopeA)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ null โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
MDC: {} // ๅฎŒๅ…จๆธ…็ฉบ

3.3 Trace ID ๅˆ†ๅ‰ฒ็ฎ—ๆณ•โ€‹

Jaeger ๆ”ฏๆŒ 128-bit ็š„ traceId๏ผŒไฝ† Java long ไป…ไธบ 64-bit๏ผŒๅ› ๆญค้œ€่ฆๅˆ†ๅ‰ฒ๏ผš

/**
* ๅฐ†ๅๅ…ญ่ฟ›ๅˆถ traceId ๅญ—็ฌฆไธฒๅˆ†ๅ‰ฒไธบ้ซ˜64ไฝๅ’ŒไฝŽ64ไฝ
*
* ็คบไพ‹:
* ่พ“ๅ…ฅ: "04bf92f3577b34da63ce929d0e0e4736" (32ไธชๅๅ…ญ่ฟ›ๅˆถๅญ—็ฌฆ = 128 bit)
* ่พ“ๅ‡บ: [0x04bf92f3577b34da, 0x63ce929d0e0e4736]
*/
public static long[] splitTraceId(String traceIdHex) {
if (traceIdHex.length() <= 16) {
// ไป…ๆœ‰ไฝŽ64ไฝ๏ผŒ้ซ˜ไฝไธบ0
return new long[]{0L, parseLong(traceIdHex)};
} else {
// ๅˆ†ๅ‰ฒไธบ้ซ˜64ไฝๅ’ŒไฝŽ64ไฝ
String highHex = traceIdHex.substring(0, traceIdHex.length() - 16);
String lowHex = traceIdHex.substring(traceIdHex.length() - 16);
return new long[]{parseLong(highHex), parseLong(lowHex)};
}
}

4. ้…็ฝฎไธŽ้›†ๆˆโ€‹

4.1 Tracer ๅˆๅง‹ๅŒ–้…็ฝฎโ€‹

static Tracer tracer = new Configuration("order-service")
.withSampler(
new Configuration.SamplerConfiguration()
.withType("const")
.withParam(1) // ้‡‡ๆ ท็އ 100%
)
.withReporter(
new Configuration.ReporterConfiguration()
.withLogSpans(true) // ๅผ€ๅ‘็Žฏๅขƒๅฏ็”จๆ—ฅๅฟ—่พ“ๅ‡บ
.withSender(
new Configuration.SenderConfiguration()
.withAgentHost("localhost")
.withAgentPort(6831)
)
)
.getTracerBuilder()
.withScopeManager(new CustomMDCScopeManager()) // ๅ…ณ้”ฎ๏ผšๆณจๅ…ฅ่‡ชๅฎšไน‰็ฎก็†ๅ™จ
.build();

4.2 Logback ้…็ฝฎโ€‹

<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [traceId=%X{traceId} spanId=%X{spanId}] - %msg%n</pattern>
</encoder>
</appender>

<appender name="JSON" class="ch.qos.logback.core.FileAppender">
<file>logs/app.json</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>spanId</includeMdcKeyName>
<includeMdcKeyName>parentId</includeMdcKeyName>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="JSON" />
</root>
</configuration>

4.3 Spring Boot ้›†ๆˆ๏ผˆๅฏ้€‰๏ผ‰โ€‹

@Configuration
public class TracingConfig {

@Bean
public Tracer jaegerTracer() {
return new Configuration(
env.getProperty("spring.application.name", "unknown-service")
)
.withSampler(samplerConfig())
.withReporter(reporterConfig())
.getTracerBuilder()
.withScopeManager(new CustomMDCScopeManager())
.build();
}

@Bean
public TracingFilter tracingFilter(Tracer tracer) {
return new TracingFilter(tracer);
}
}

5. ไฝฟ็”จๅœบๆ™ฏไธŽๆœ€ไฝณๅฎž่ทตโ€‹

5.1 ๆŽฅๆ”ถไธŠๆธธ Trace ไธŠไธ‹ๆ–‡โ€‹

@RestController
public class OrderController {

@GetMapping("/order/{id}")
public Order getOrder(
@PathVariable String id,
@RequestHeader(value = "uber-trace-id", required = false) String uberTraceId
) {
JaegerSpanContext parentContext = parseUberTraceId(uberTraceId);

Span span = tracer.buildSpan("get-order")
.asChildOf(parentContext) // ๅ…ณ้”ฎ๏ผš้“พๆŽฅ่ฟœ็จ‹็ˆถ Span
.withTag(Tags.SPAN_KIND, Tags.SPAN_KIND_SERVER)
.withTag("order.id", id)
.start();

try (Scope scope = tracer.scopeManager().activate(span)) {
log.info("Processing order request"); // ่‡ชๅŠจๆบๅธฆ traceId/spanId
return orderService.getById(id);
} finally {
span.finish();
}
}
}

5.2 ๅ‘ไธ‹ๆธธไผ ้€’ Trace ไธŠไธ‹ๆ–‡โ€‹

public class PaymentClient {

public void processPayment(String orderId) {
Span span = tracer.buildSpan("call-payment-service")
.withTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CLIENT)
.start();

try (Scope scope = tracer.scopeManager().activate(span)) {
HttpHeaders headers = new HttpHeaders();

// ๆณจๅ…ฅ trace ไธŠไธ‹ๆ–‡ๅˆฐ HTTP Header
tracer.inject(
span.context(),
Format.Builtin.HTTP_HEADERS,
new HttpHeadersCarrier(headers)
);

restTemplate.exchange(
"http://payment-service/pay",
HttpMethod.POST,
new HttpEntity<>(paymentRequest, headers),
PaymentResponse.class
);
} finally {
span.finish();
}
}
}

5.3 ๅผ‚ๆญฅๅœบๆ™ฏๅค„็†โ€‹

@Service
public class AsyncOrderService {

@Autowired
private Tracer tracer;

@Autowired
private ExecutorService executorService;

public void processOrderAsync(String orderId) {
Span parentSpan = tracer.activeSpan(); // ่Žทๅ–ๅฝ“ๅ‰ Span

executorService.submit(() -> {
// ๅœจๆ–ฐ็บฟ็จ‹ไธญ้‡ๆ–ฐๆฟ€ๆดป็ˆถ Span
Span asyncSpan = tracer.buildSpan("async-process")
.asChildOf(parentSpan)
.start();

try (Scope scope = tracer.scopeManager().activate(asyncSpan)) {
log.info("Async processing order"); // MDC ๆญฃ็กฎๆณจๅ…ฅ
// ไธšๅŠก้€ป่พ‘
} finally {
asyncSpan.finish();
}
});
}
}

6. ๅ…ณ้”ฎ่ฎพ่ฎกๅ†ณ็ญ–โ€‹

6.1 ไธบไป€ไนˆไธ็›ดๆŽฅๅœจไธšๅŠกไปฃ็ ไธญๆ“ไฝœ MDC๏ผŸโ€‹

้—ฎ้ข˜๏ผšๆ‰‹ๅŠจ็ฎก็†ๅฎนๆ˜“้—ๆผๆธ…็†๏ผŒๅฏผ่‡ด็บฟ็จ‹ๆฑ ๅค็”จๆ—ถ traceId ๆฑกๆŸ“

่งฃๅ†ณๆ–นๆกˆ๏ผš้€š่ฟ‡ ScopeManager ็š„็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†๏ผŒๅœจ activate() ๆ—ถๆณจๅ…ฅ๏ผŒๅœจ close() ๆ—ถ่‡ชๅŠจๆธ…็†

6.2 ไธบไป€ไนˆ้œ€่ฆไฟๅญ˜ MDC ๅฟซ็…ง๏ผŸโ€‹

ๅœบๆ™ฏ๏ผšๅตŒๅฅ— Span ่ฐƒ็”จๆ—ถ๏ผŒ้œ€่ฆๅœจๅญ Span ็ป“ๆŸๅŽๆขๅค็ˆถ Span ็š„ MDC

ๅฎž็Žฐ๏ผš

// ่ฟ›ๅ…ฅๅญ Span ๆ—ถ
this.previousTraceId = MDC.get("traceId"); // ไฟๅญ˜ๅฟซ็…ง
MDC.put("traceId", childSpan.context().toTraceId()); // ่ฆ†็›–

// ้€€ๅ‡บๅญ Span ๆ—ถ
restoreMDC("traceId", previousTraceId); // ๆขๅคๅฟซ็…ง

6.3 ไธบไป€ไนˆไฝฟ็”จ ThreadLocal ่€Œไธๆ˜ฏ InheritableThreadLocal๏ผŸโ€‹

่€ƒ้‡๏ผš

  • ThreadLocal๏ผšไธฅๆ ผ็บฟ็จ‹้š”็ฆป๏ผŒ้€‚ๅˆๅŒๆญฅๅœบๆ™ฏ
  • InheritableThreadLocal๏ผšๅญ็บฟ็จ‹็ปงๆ‰ฟ็ˆถ็บฟ็จ‹ๅ€ผ๏ผŒไฝ†ๅœจ็บฟ็จ‹ๆฑ ๅค็”จๆ—ถๅฎนๆ˜“ๅ‡บ็ŽฐไธŠไธ‹ๆ–‡ๆณ„ๆผ

ๆŽจ่๏ผšๅผ‚ๆญฅๅœบๆ™ฏๆ˜พๅผไผ ้€’ Span๏ผŒ่€Œ้žไพ่ต–่‡ชๅŠจ็ปงๆ‰ฟ

7. ็›‘ๆŽงไธŽ่ฐƒ่ฏ•โ€‹

7.1 ๆ—ฅๅฟ—่พ“ๅ‡บ็คบไพ‹โ€‹

21:45:32.123 [http-nio-8080-exec-1] INFO c.w.OrderService [traceId=04bf92f3577b34da63ce929d0e0e4736 spanId=36bd32b7a5712a1a] - Processing order ORD-001
21:45:32.234 [http-nio-8080-exec-1] INFO c.w.PaymentClient [traceId=04bf92f3577b34da63ce929d0e0e4736 spanId=7f3a28b9c4d5e6a1] - Calling payment service
21:45:32.456 [http-nio-8080-exec-1] INFO c.w.OrderService [traceId=04bf92f3577b34da63ce929d0e0e4736 spanId=36bd32b7a5712a1a] - Order processed successfully

7.2 Jaeger UI ๆŸฅ่ฏขโ€‹

ๅœจ Jaeger UI ไธญๅฏไปฅ๏ผš

  1. ้€š่ฟ‡ traceId 04bf92f3577b34da63ce929d0e0e4736 ๆŸฅ็œ‹ๅฎŒๆ•ด่ฐƒ็”จ้“พ
  2. ๆŸฅ็œ‹ๆฏไธช Span ็š„ Tags๏ผˆๅฆ‚ db.statement, http.status_code๏ผ‰
  3. ๆŸฅ็œ‹ Span ไน‹้—ด็š„็ˆถๅญๅ…ณ็ณปๅ’Œ่€—ๆ—ถๅˆ†ๅธƒ

7.3 ๅธธ่ง้—ฎ้ข˜ๆŽ’ๆŸฅโ€‹

้—ฎ้ข˜็Žฐ่ฑกๅฏ่ƒฝๅŽŸๅ› ๆŽ’ๆŸฅๆ–นๆณ•
ๆ—ฅๅฟ—ไธญ traceId ไธบ็ฉบๆœชๆฟ€ๆดป Span ๆˆ– ScopeManager ๆœชๆญฃ็กฎ้…็ฝฎๆฃ€ๆŸฅ activate() ่ฐƒ็”จๅ’Œ Tracer ๆž„ๅปบ
traceId ๅœจไธๅŒ่ฏทๆฑ‚้—ดไธฒๅฐMDC ๆœชๆญฃ็กฎๆธ…็†๏ผˆ็บฟ็จ‹ๆฑ ๅค็”จ๏ผ‰็กฎไฟ finally ๅ—ไธญ่ฐƒ็”จ MDC.remove()
ๅญ Span ๆœช้“พๆŽฅๅˆฐ็ˆถ SpanasChildOf() ๅ‚ๆ•ฐ้”™่ฏฏ้ชŒ่ฏ parentSpanId ๆ˜ฏๅฆๆญฃ็กฎ่งฃๆž
Jaeger ไธญ็œ‹ไธๅˆฐ Span้‡‡ๆ ท็އ่ฎพ็ฝฎไธบ0ๆˆ– Reporter ้…็ฝฎ้”™่ฏฏๆฃ€ๆŸฅ withParam(1) ๅ’Œ็ฝ‘็ปœ่ฟž้€šๆ€ง

8. ๆ€ง่ƒฝ่€ƒ้‡โ€‹

8.1 ๆ€ง่ƒฝๅฝฑๅ“ๅˆ†ๆžโ€‹

ๆ“ไฝœ่€—ๆ—ถๅฝฑๅ“
MDC.put()< 1ฮผsๅฏๅฟฝ็•ฅ
ThreadLocal.get()< 1ฮผsๅฏๅฟฝ็•ฅ
Span.start()~10ฮผsไฝŽ
Span.finish() + ไธŠๆŠฅ~100ฮผsๅผ‚ๆญฅไธŠๆŠฅ๏ผŒๅฏนไธปๆต็จ‹ๅฝฑๅ“ๅฐ

8.2 ไผ˜ๅŒ–ๅปบ่ฎฎโ€‹

  1. ๅˆ็†ๆŽงๅˆถ้‡‡ๆ ท็އ๏ผš็”Ÿไบง็Žฏๅขƒๅฏ่ฎพ็ฝฎไธบ 0.1๏ผˆ10%๏ผ‰ไปฅ้™ไฝŽๅญ˜ๅ‚จๆˆๆœฌ
  2. ๆ‰น้‡ไธŠๆŠฅ๏ผšReporter ้…็ฝฎ withFlushInterval(1000) ๆ‰น้‡ๅ‘้€ Span
  3. ้ฟๅ…่ฟ‡ๅบฆ Span๏ผšไธ่ฆไธบๆฏไธชๆ•ฐๆฎๅบ“ๆŸฅ่ฏข้ƒฝๅˆ›ๅปบ Span๏ผŒๆŽงๅˆถ็ฒ’ๅบฆ

9. ๆ‰ฉๅฑ•ๆ–นๅ‘โ€‹

9.1 ๆ”ฏๆŒๅ“ๅบ”ๅผ็ผ–็จ‹๏ผˆReactor/WebFlux๏ผ‰โ€‹

public class ReactorScopeManager implements ScopeManager {
@Override
public Scope activate(Span span) {
return new ReactorScope(span);
}

static class ReactorScope implements Scope {
ReactorScope(Span span) {
// ไฝฟ็”จ Reactor Context ่€Œ้ž ThreadLocal
Context.of("span", span);
}
}
}

9.2 ้›†ๆˆ Spring Cloud Sleuthโ€‹

Spring Cloud Sleuth ๆไพ›ไบ†ๅผ€็ฎฑๅณ็”จ็š„ๅˆ†ๅธƒๅผ่ฟฝ่ธช๏ผŒๅฏๆ›ฟไปฃๆœฌๆ–นๆกˆ๏ผš

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

9.3 ๆ”ฏๆŒ OpenTelemetryโ€‹

OpenTelemetry ๆ˜ฏๆ–ฐไธ€ไปฃๅฏ่ง‚ๆต‹ๆ€งๆ ‡ๅ‡†๏ผŒๅปบ่ฎฎๆœชๆฅ่ฟ็งป๏ผš

OpenTelemetry openTelemetry = AutoConfiguredOpenTelemetrySdk
.initialize()
.getOpenTelemetrySdk();

10. ๆ€ป็ป“โ€‹

ๆœฌๆ–นๆกˆ้€š่ฟ‡่‡ชๅฎšไน‰ ScopeManager ๅฎž็Žฐไบ† Jaeger Trace ไธŽ SLF4J MDC ็š„ๆ— ็ผ้›†ๆˆ๏ผŒๅ…ทๅค‡ไปฅไธ‹็‰น็‚น๏ผš

โœ… ่‡ชๅŠจๅŒ–๏ผšScope ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†๏ผŒๆ— ้œ€ๆ‰‹ๅŠจๆ“ไฝœ MDC
โœ… ็บฟ็จ‹ๅฎ‰ๅ…จ๏ผšThreadLocal ้š”็ฆป + ๅฟซ็…งๆขๅคๆœบๅˆถ
โœ… ๅตŒๅฅ—ๆ”ฏๆŒ๏ผš้“พ่กจ็ป“ๆž„็ปดๆŠคๅคšๅฑ‚ Span ๅ…ณ็ณป
โœ… ็”Ÿไบงๅฏ็”จ๏ผšๅทฒ่€ƒ่™‘็บฟ็จ‹ๆฑ ๅค็”จใ€ๅผ‚ๆญฅๅœบๆ™ฏ็ญ‰่พน็•Œๆƒ…ๅ†ต

่ฏฅๆ–นๆกˆ้€‚็”จไบŽ้œ€่ฆ็ฒพ็ป†ๆŽงๅˆถ trace ไธŠไธ‹ๆ–‡ไผ ๆ’ญ็š„ๅพฎๆœๅŠกๆžถๆž„๏ผŒ็‰นๅˆซๆ˜ฏ้œ€่ฆๆŽฅๆ”ถไธŠๆธธ trace ไฟกๆฏ็š„ๆœๅŠก็ฝ‘ๅ…ณใ€BFF ๅฑ‚็ญ‰ๅœบๆ™ฏใ€‚