index

annotations

@NewSpan

진행 중인 trace가 있다면 기존 span의 자식 span을 만들고, 없다면 새로운 span을 만드는 어노테이션

public 메서드에만 선언할 수 있으며 메서드 파라미터에 @SpanTag 어노테이션을 적용해서 파라미터 값을 span의 태그로 추가할 수 있다

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.METHOD)
public @interface NewSpan {

    String name() default "";
    String value() default "";
}

@ContinueSpan

새로운 span을 생성하지 않고 기존 span을 이어가는 어노테이션

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.METHOD)
public @interface ContinueSpan {
    String log() default "";
}

@SpanTag

메서드 파라미터를 span 태그로 추가하는 어노테이션

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.PARAMETER)
public @interface SpanTag {
    String value() default "";
    String key() default "";
    String expression() default "";
    Class<? extends ValueResolver> resolver() default NoOpValueResolver.class;
}

Tracer

trace 또는 span을 생성, 시작, 활성화하거나 관리(builder, customizer) 객체를 반환하고 현재 활성화된 span을 조회하는 api

public interface Tracer extends BaggageManager {
    
    // Tracer NOOP ... 
    
    
    /* ----------  trace 및 span 생성 ---------- */
    
    // scope에 있는 현재 span을 기반으로 자식 span 생성
    // span이 없는 경우 새로운 trace를 생성한다
    Span nextSpan();

    // 주어진 parent span에 대한 자식 span 생성
    // parent가 null인 경우 새로운 trace를 생성한다
    Span nextSpan(@Nullable Span parent);

    // 주어진 span을 활성 span(current span)으로 설정한다
    // SpanInScope 객체를 반환하는데, 이는 현재 span의 scope를 나타낸다
    // SpanInScope는 자동으로 scope를 닫기 위해 AutoCloseable을 구현하고 있으므로 try-with-resources 패턴으로 사용하면 메모리 누수를 방지할 수 있다
    Tracer.SpanInScope withSpan(@Nullable Span span);

    // 활성화된 span이 있으면 (currentSpan 메서드에서 null을 반환하지 않으면) 자식 span을 생성 및 시작하고
    // 없으면 새 trace를 생성한 뒤 span을 생성 및 시작한다
    // 반환 값인 ScopedSpan은 end 메서드를 호출하기 전까지 활성화된 상태를 유지한다
    ScopedSpan startScopeSpan(String name);

    
    /* ----------  활성화된 current span 확인  ---------- */
    
    // scope에 있는 활성화된 current span 반환
    // 없는 경우 null 반환
    @Nullable
    Span currentSpan();
    
    
    /* ----------  span 및 trace 관리 객체 반환 ---------- */
    
    Span.Builder spanBuilder();

    TraceContext.Builder traceContextBuilder();

    CurrentTraceContext currentTraceContext();

    @Nullable
    SpanCustomizer currentSpanCustomizer();

    
    /* ----------- span의 scope 지정 (autocloseable) ----------- */
    
    interface SpanInScope extends Closeable {
        
        // scope가 닫힐 때 리소스 정리(MDC 정리 등)
        @Override
        void close();

    }
}

TraceContext

trace와 span 데이터를 보관하는 기능을 추상화한 인터페이스

public interface TraceContext {

    // TraceContext NOOP ...
    // Builder ...
    
    // span의 trace id 반환
    String traceId();
    
    // 부모 span id 반환 (없는 경우 null 반환)
    // span은 트리 구조를 가질 수 있다
    @Nullable
    String parentId();
    
    // 현재 span id 반환
    String spanId();
    
    // 해당 데이터가 수집될 지 여부를 반환한다
    // true: 수집됨, false: 수집 안됨, null: 아직 결정 안됨
    // false인 경우 로컬에서 span을 생성하지만 exporter를 통해 외부로 전달되지 않는다
    Boolean sampled();
    
}

CurrentTraceContext

주어진 span을 scope의 활성 span으로 만드는 api

public interface CurrentTraceContext {

    
    /* --------- 활성화된 context 조회 --------- */
    
    // 현재 trace context를 반환하거나 없는 경우 null 반환
    @Nullable
    TraceContext context();

    
    /* --------- span 활성화 --------- */
    
    // 주어진 context를 scope에 활성 span으로 설정한다 (반환 값이 닫히기 전까지 활성화됨)
    // null을 주면 scope를 비워둔다
    // CurrentTraceContext.Scope는 autocloseable을 구현하고 있으므로 try-catch-resources 구문 사용 권장 
    CurrentTraceContext.Scope newScope(@Nullable TraceContext context);

    // 주어진 context가 이미 scope에 있다면 noop scope 인스턴스를 반환한다
    // 나머진 newScope와 동일하게 동작한다
    CurrentTraceContext.Scope maybeScope(@Nullable TraceContext context);

    
    /* --------- trace에 포함될 작업을 지정하는 api (비동기 작업 포함) --------- */
    
    <C> Callable<C> wrap(Callable<C> task);
    Runnable wrap(Runnable task);
    Executor wrap(Executor delegate);
    ExecutorService wrap(ExecutorService delegate);

    
    /* ----------- span의 scope 지정 (autocloseable) ----------- */
    
    interface Scope extends Closeable {
        
        Scope NOOP = () -> {};

        // scope가 닫힐 때 리소스 정리(MDC 정리 등)
        @Override
        void close();

    }
}

Span

해당 서비스에서 수행하는 작업 단위에 대해 추상화를 한 객체로, 유연성과 확장성을 위해 역할 별로 책임 분리를 해놓았다

Span

span은 하나의 작업 단위를 나타내며 시작과 종료를 할 수 있으며 시간 정보(timing information)과 이벤트 및 태그를 포함한다

동적으로 이름과 이벤트 및 태그를 커스터마이징할 수 있는 SpanCustomizer 인터페이스를 확장한다

public interface Span extends io.micrometer.tracing.SpanCustomizer {
    
    // Span NOOP ...
    // Builder ...
    
    // 기록되지 않고 아무것도 보고하지 않는 span이지만 외부 요청에 포함되는 경우 true 반환
    boolean isNoop();
    
    // 이 span 인스턴스에 대한 context 반환
    TraceContext context();
    
    // span 시작
    Span start();
    
    // span 이름 지정
    Span name(String name);
    
    // span 이벤트 지정
    Span event(String value);
    Span event(String value, long time, TimeUnit timeUnit);
    
    // span 태그 지정
    Span tag(String key, String value);

    // span 에러 기록
    Span error(Throwable throwable);
    
    // span 종료, noop이 아니라면 span을 멈추고 기록한다
    void end();
    void end(long time, TimeUnit timeUnit);
    
    // span을 종료하지만 기록하지 않는다
    void abandon();

    // 원격 정보 설정
    Span remoteServiceName(String remoteServiceName);
    Span remoteIpAndPort(String ip, int port);
    
    // span 타입 설정
    // span 간의 부모-자식 관계 외에도 추가 관계를 지정하는 데 사용될 수 있다
    // opentelemetry 참고
    enum Kind {
        
        // rpc 또는 기타 원격 요청을 서버에서 처리하는 span
        SERVER,
        
        // rpc 또는 기타 원격 요청으로 감싼 클라이언트의 span
        CLIENT,
        
        // broker에 메시지를 송신하는 producer span
        // producer와 consumer span은 직접적으로 중요한 관계를 가지지 않는다 
        PRODUCER,
        
        // broker로부터 메시지를 수신하는 consumer span
        // producer와 consumer span은 직접적으로 중요한 관계를 가지지 않는다
        CONSUMER
    }
}

SpanCustomizer

scope에 감싸진 현재 span에 대한 커스터마이징 기능을 제공하는 api

public interface SpanCustomizer {
    
    // SpanCustomizer NOOP ...

    // span 이름 커스터마이징
    SpanCustomizer name(String name);

    // span 태그 커스터마이징
    SpanCustomizer tag(String key, String value);

    // span 이벤트 커스터마이징
    SpanCustomizer event(String value);
}

SpanNamer

span 이름을 자동으로 생성하는 api

public interface SpanNamer {

    // 생성할 span 이름과 관련된 객체와 기본 값을 매개변수로 받는다 
    String name(Object object, String defaultValue);
}

ScopedSpan

span.start 메서드를 호출한 후, end 메서드를 호출하기 전까지 현재 span을 나타내는 api

public interface ScopedSpan {
    
    // ScopedSpan NOOP ...

    // 기록되지 않고 아무것도 보고하지 않는 span이지만 외부 요청에 포함되는 경우 true 반환
    boolean isNoop();

    // 이 span 인스턴스에 대한 context 반환
    TraceContext context();

    // span 이름 지정
    Span name(String name);

    // span 태그 지정
    ScopedSpan tag(String key, String value);

    // span 이벤트 지정
    ScopedSpan event(String value);

    // span 에러 지정
    ScopedSpan error(Throwable throwable);

    // span 종료, noop이 아니라면 span을 멈추고 기록한다
    void end();
}

ThreadLocalSpan

스레드 로컬에 저장된 span을 나타내는 클래스

public class ThreadLocalSpan {

    private final ThreadLocal<ArrayDeque<SpanAndScope>> currentSpanInScopeStack = new ThreadLocal<>();
    
    private final Tracer tracer;

    public ThreadLocalSpan(Tracer tracer) {
        this.tracer = tracer;
    }

    // 스레드 로컬에 span, scope 추가
    public void set(Span span) {
        Tracer.SpanInScope spanInScope = this.tracer.withSpan(span);
        SpanAndScope newSpanAndScope = new SpanAndScope(span, spanInScope);
        getCurrentSpanInScopeStack().addFirst(newSpanAndScope);
    }

    // 현재 span 및 scope 반환
    public SpanAndScope get() {
        return getCurrentSpanInScopeStack().peekFirst();
    }

    // 스레드 로컬에 있는 현재 span 삭제
    // 아무것도 없으면 null을 반환하고, span의 scope가 열려있는 경우 닫아준다
    public SpanAndScope remove() {
        SpanAndScope spanAndScope = getCurrentSpanInScopeStack().pollFirst();
        if (spanAndScope == null) {
            return null;
        }
        if (spanAndScope.getScope() != null) {
            spanAndScope.getScope().close();
        }
        return spanAndScope;
    }
}

SpanAndScope

span과 Tracer.SpanInScope를 포함한 클래스

public class SpanAndScope implements Closeable {

    private final Span span;

    private final Tracer.SpanInScope scope;

    public SpanAndScope(Span span, @Nullable Tracer.SpanInScope scope) {
        this.span = span;
        this.scope = scope;
    }
    
    // getter

    // scope를 닫고 span을 종료한다
    @Override
    public void close() {
        if (this.scope != null) {
            this.scope.close();
        }
        this.span.end();
    }
}

Handler

micrometer tracing에서 micrometer observation의 ObservationHandler의 기능을 사용할 수 있도록 확장 인터페이스와 그 구현체를 제공한다

인터페이스: TracingObservationHandler

구현체: DefaultTracingObservationHandler, TracingAwareMeterObservationHandler 등

Baggage

Baggage는 분산 추적(distributed tracing)에서 요청 간에 전달되는 key-value 형태의 메타데이터로 TraceContext와 함께 전파되어 서비스 간의 추가적인 데이터를 공유할 수 있게 해준다

사용자 정보 전달(userId, tenantId 등), 요청 우선순위(priority, debug 플래그), A/B 테스트 그룹 정보, 트랜잭션 데이터 전달 등의 용도로 사용될 수 있다

tracing 구현체에 따라 Baggage를 scope로 감싸야 하거나 불변 객체로 만든다 (opentelemetry)

public interface Baggage extends BaggageView {
    
    // Baggage NOOP ...

    // baggage value 설정 - makeCurrent(String) 사용 권장
    @Deprecated
    Baggage set(String value);

    // 주어진 traceContext에 대한 baggage value 설정 - makeCurrent(TraceContext, String) 사용 권장 
    @Deprecated
    Baggage set(TraceContext traceContext, String value);
    
    // scope의 활성 baggage로 설정하고 BaggageInScope 인스턴스를 반환한다
    BaggageInScope makeCurrent();

    // scope의 활성 baggage로 설정하고 값을 설정
    default BaggageInScope makeCurrent(String value) {
        return set(value).makeCurrent();
    }

    // 주어진 traceContext에 대한 baggage를 활성화하고 값을 설정
    default BaggageInScope makeCurrent(TraceContext traceContext, String value) {
        return set(traceContext, value).makeCurrent();
    }
}

BaggageView

Baggage에 대한 정보를 제공하는 인터페이스

public interface BaggageView {
    
    // BaggageView NOOP ...

    // baggage 이름 반환
    String name();
    
    // baggage 값 반환
    @Nullable
    String get();
    
    // 주어진 TraceContext에 대한 baggage 값 반환 
    @Nullable
    String get(TraceContext traceContext);
}

BaggageInScope

일부 tracer는 baggage를 감싸는 scope를 필요로 한다

활성화된 baggage의 작업이 종료되면 scope를 자동으로 닫을 수 있게 BaggageInScope 추상화를 제공한다

public interface BaggageInScope extends BaggageView, Closeable {
    
    // BaggageInScope NOOP ...

    @Override
    void close();
}

BaggageManager

Baggage를 관리하는 객체로 scope에 있는 baggage를 되찾거나 새로 추가하는 기능을 제공한다

public interface BaggageManager {
    
    // BaggageManager NOOP ...
    
    // 모든 baggage 엔트리 반환
    Map<String, String> getAllBaggage();

    // 주어진 이름을 가진 baggage 조회 
    @Nullable
    Baggage getBaggage(String name);

    // 주어진 traceContext와 name에 대한 baggage 조회
    @Nullable
    Baggage getBaggage(TraceContext traceContext, String name);

    // 주어진 이름에 대한 새로운 baggage 생성 또는 기존 baggage 반환
    // createBaggageInScope(String, String) 사용 권장
    @Deprecated
    Baggage createBaggage(String name);

    // 주어진 이름과 값에 대한 새로운 baggage 생성 또는 기존 baggage 반환
    // createBaggageInScope(String, String) 사용 권장
    @Deprecated
    Baggage createBaggage(String name, String value);

    // 주어진 이름과 값을 가진 새로운 baggage를 생성하고 활성화시킨다
    default BaggageInScope createBaggageInScope(String name, String value) {
        return createBaggage(name).makeCurrent(value);
    }

    // 주어진 traceContext, 이름과 값을 가진 새로운 baggage를 생성하고 활성화시킨다
    default BaggageInScope createBaggageInScope(TraceContext traceContext, String name, String value) {
        return createBaggage(name).makeCurrent(traceContext, value);
    }

    // baggage 필드의 모든 이름 반환
    default List<String> getBaggageFields() {
        return Collections.emptyList();
    }
}