욱'S 노트

마이크로미터 오픈텔레메트리 집킨 Exporter 해보기 본문

Programming/Micrometer

마이크로미터 오픈텔레메트리 집킨 Exporter 해보기

devsun 2025. 3. 7. 16:50
반응형

마이크로미터 오픈텔레메트리 기본 세팅은 지난 시간에 해봤었다. 그럼 Tracing API에 대해서 조금 더 알아보자.

 

예제 코드 

먼저 기본 세팅은 그대로 유지한다.

import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext
import io.micrometer.tracing.otel.bridge.OtelTracer
import io.micrometer.tracing.otel.bridge.Slf4JEventListener
import io.opentelemetry.sdk.OpenTelemetrySdk


fun main() {
    // [OTel component] The SDK implementation of OpenTelemetry
    val openTelemetrySdk: OpenTelemetrySdk = OpenTelemetrySdk.builder()
        .build()

    // [OTel component] Tracer is a component that handles the life-cycle of a span
    val otelTracer = openTelemetrySdk.tracerProvider.get("io.micrometer.test")

    // [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel
    val otelCurrentTraceContext = OtelCurrentTraceContext()

    // [Micrometer Tracing component] A Micrometer Tracing listener for setting up MDC
    val slf4JEventListener = Slf4JEventListener()

    // [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel's Tracer.
    val tracer = OtelTracer(otelTracer, otelCurrentTraceContext, { event ->
        slf4JEventListener.onEvent(event)
    })
    
    // ... 후략 ...

 

다음은 스펜이 이미 시작 중인데 다음 스펜을 붙이는 코드이다.

    val initSpan = tracer.nextSpan().name("calculateTax")
    val newSpan = tracer.nextSpan(initSpan).name("calculateCommission")

    try {
        tracer.withSpan(newSpan.start())

        newSpan.tag("taxValue", "100")
        newSpan.event("taxCalculated")
    } finally {
        newSpan.end()
        initSpan.end()
    }

    println(initSpan)
    println(newSpan)

 

출력된 결과를 보면 같은 트레이스ID에 부모 스펜으로 이전 스팬이 참조되는 걸 확인 할 수 있다.

SdkSpan{traceId=766e4849fd3dec873a104dce4acaafad, spanId=64266dc2fb8d480f, parentSpanContext=ImmutableSpanContext{traceId=00000000000000000000000000000000, spanId=0000000000000000, traceFlags=00, traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=false}, name=calculateTax, kind=INTERNAL, attributes=null, status=ImmutableStatusData{statusCode=UNSET, description=}, totalRecordedEvents=0, totalRecordedLinks=0, startEpochNanos=1741332382998919000, endEpochNanos=1741332388853309542}
SdkSpan{traceId=766e4849fd3dec873a104dce4acaafad, spanId=b9a9621a2e66e564, parentSpanContext=ImmutableSpanContext{traceId=766e4849fd3dec873a104dce4acaafad, spanId=64266dc2fb8d480f, traceFlags=01, traceState=ArrayBasedTraceState{entries=[]}, remote=false, valid=true}, name=calculateCommission, kind=INTERNAL, attributes=AttributesMap{data={taxValue=100}, capacity=128, totalAddedValues=1}, status=ImmutableStatusData{statusCode=UNSET, description=}, totalRecordedEvents=1, totalRecordedLinks=0, startEpochNanos=1741332383001096083, endEpochNanos=1741332387238214750}

 

로컬에서 Zipkin 실행하기 

집킨 공식 사이트를 보면 Getting Started에 가장 먼저 나오는 방식이 도커를 이용하는 방식이다. 아래의 명령을 사용해서 zipkin 이미지를 로컬에 설치하고 실행시켜보자.

docker pull openzipkin/zipkin
docker run -p 9411:9411 openzipkin/zipkin

 

 

브라우저에서 http://localhost:9411/zipkin/ 로 접속하면 다음가 같은 화면을 확인 할 수 있다.

 

Zipkin으로 Tracing 정보 보내기

Zipkin으로 연동하기 위해서 다음과 같은 설정 코드가 추가된다. 아래의 코드는 사실 프로덕션 레벨에서는 쓸 수 없다. 이유는 성능 때문이다. 보통은 즉시 전송보다는 배치 전송을 http보다는 kafka등의 큐를 이용한다.

fun main() {
    // 트레이싱 정보를 Zipkin으로 전송하기 세팅 Endpoint를 지정하면 OkHttp을 이용하여 전송
    val spanExporter: SpanExporter = ZipkinSpanExporterBuilder()
        .setEndpoint("http://localhost:9411/api/v2/spans")
        .build()
    
    // 단순한 스팬 처리기 즉시 전송한다.
    val build = SimpleSpanProcessor.builder(spanExporter).build()
    
    // 샘플링이랑 서비스 이름등을 설정
    val sdkTracerProvider = SdkTracerProvider.builder()
        .setSampler(alwaysOn())
        .addResource(Resource.create(Attributes.of(AttributeKey.stringKey("service.name"), "my-service")))
        .addSpanProcessor(SimpleSpanProcessor.builder(spanExporter).build())
        .build()

    // B3 Propagation은 Tracing Propagation의 스펙 중 하나, 헤더에 "X-B3-"로 시작 
    val propagator: ContextPropagators = ContextPropagators.create(B3Propagator.injectingSingleHeader())

    val openTelemetrySdk: OpenTelemetrySdk = OpenTelemetrySdk.builder()
        .setTracerProvider(sdkTracerProvider)
        .setPropagators(propagator)
        .build()

    // [OTel component] Tracer is a component that handles the life-cycle of a span
    val otelTracer = openTelemetrySdk.tracerProvider.get("io.micrometer.test")

    // [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel
    val otelCurrentTraceContext = OtelCurrentTraceContext()

    // [Micrometer Tracing component] A Micrometer Tracing listener for setting up MDC
    val slf4JEventListener = Slf4JEventListener()

    // [Micrometer Tracing component] A Micrometer Tracing wrapper for OTel's Tracer.
    val tracer = OtelTracer(otelTracer, otelCurrentTraceContext, { event ->
        slf4JEventListener.onEvent(event)
    })
    
    ... 중략 ...

 

다시 예제코드를 수행해보자. 화면에 출력된 트레이스 아이디를 집킨에서 조회를 할 수 있다.

 

참고자료 : https://docs.micrometer.io/tracing/reference/api.html

 

Using Micrometer Tracing Directly :: Micrometer Tracing

Micrometer Tracing contains @NewSpan, @ContinueSpan, and @SpanTag annotations that frameworks can use to create or customize spans for either specific types of methods such as those serving web request endpoints or, more generally, to all methods. Micromet

docs.micrometer.io

 

반응형