토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 클라우드 슬루스 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.

이번 섹션에선 Spring Cloud Sleuth의 다양한 기능들을 커스텀하는 방법을 설명한다. Spring Cloud Sleuth가 생성하는 span, 태그, 이벤트 목록은 부록을 참고해라.

목차


6.1. Apache Kafka

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 카프카 클라이언트(KafkaProducer, KafkaConsumer)에 데코레이터 패턴을 적용해 이벤트를 생산produce하거나 컨슘consume할 때마다 span을 생성한다. 이 기능은 spring.sleuth.kafka.enabled 값을 false로 설정하면 비활성화할 수 있다.

이 데코레이터 패턴을 Sleuth의 자동 설정으로 적용하려면 Producer 또는 Consumer를 빈으로 등록해야 한다. 그리고 이 빈들을 주입할 때는 타입을 Producer 또는 Consumer로 지정해야 한다 (e.g. KafkaProducer가 아니라).

프로젝트 리액터의 경우, 정의된 모든 KafkaReceiver<K,V> 타입 빈을 TracingKafkaReceiver<K,V>로 감싼다. 이렇게 하면 수신하는 요소마다 자체 트레이싱tracing 컨텍스트가 전파되어 별도의 퍼블리셔가 생성된다. 리액터 계측instrumentation과 함께 사용하면 span의 컨텍스트에 접근할 수 있다.

부모 컨텍스트가 없는 경우, 새 trace-id를 사용해 자식 span만 생성한다.

@Bean
KafkaReceiver<K, V> reactiveKafkaReceiver(ReceiverOptions<K,V> options) {
    return KafkaReceiver.create(options);
}

이후 요소들을 수신하기 시작하면 컨텍스트를 처리할 수 있다.

@Bean
DisposableBean exampleRunningConsumer(KafkaReceiver<String, String> receiver){
    reactor.core.Disposable disposable = receiver.receive()
        //If you need to read context you can for example use deferContextual
        .flatMap(record -> Mono.deferContextual(context -> Mono.just(record)))
        .doOnNext(record -> log.info("I will be coorelated to the child span created with parent context from kafka record"))
        .subscribe(record -> record.receiverOffset().acknowledge());

    return disposable::dispose;
}

6.2. Asynchronous Communication

이번 섹션에선 Spring Cloud Sleuth에서 비동기 통신을 커스텀하는 방법에 대해 설명한다.

6.2.1. @Async Annotated methods

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Spring Cloud Sleuth는 비동기 통신과 관련된 구성 요소들을 계측instrumentation하기 때문에, 트레이싱tracing 정보는 여러 스레드를 오갈 때도 잘 전달된다. 이 동작은 spring.sleuth.async.enabled 값을 false로 설정하면 비활성화할 수 있다.

메소드 위에 @Async 애노테이션을 선언하면, 기존 Span은 자동으로 다음과 같이 변경된다:

기존 span을 수정하는 것이기 때문에 원래 이름을 유지하고 싶다면 (e.g. HTTP 요청을 받아 span이 만들어진 경우 등) , @Async를 선언한 메소드를 @NewSpan 애노테이션으로 감싸거나 span을 직접 새로 만들어야 한다.

6.2.2. @Scheduled Annotated Methods

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Spring Cloud Sleuth는 스케줄링된 메소드가 실행될 때도 이를 계측instrumentation하기 때문에, 트레이싱tracing 정보는 여러 스레드를 오갈 때도 잘 전달된다. 이 동작은 spring.sleuth.scheduled.enabled 값을 false로 설정하면 비활성화할 수 있다.

메소드 위에 @Scheduled 애노테이션을 선언하면, 다음과 같은 span을 자동으로 생성해준다:

@Scheduled를 선언한 클래스 중 span을 만들고 싶지 않은 클래스도 있다면, spring.sleuth.scheduled.skipPattern을 해당 클래스의 풀 네임fully qualified name과 매칭되는 정규식으로 설정해주면 된다.

6.2.3. Executor, ExecutorService, and ScheduledExecutorService

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 LazyTraceExecutor, TraceableExecutorService, TraceableScheduledExecutorService를 제공한다. 이 구현체들은 새 태스크를 제출submit하거나, 실행하거나, 스케줄링될 때마다 span을 생성한다.

다음은 CompletableFuture로 코드를 작성하면서 TraceableExecutorService로 트레이싱tracing 정보를 전달하는 예시다:

CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(() -> {
    // perform some logic
    return 1_000_000L;
}, new TraceableExecutorService(beanFactory, executorService,
        // 'calculateTax' explicitly names the span - this param is optional
        "calculateTax"));

Sleuth는 그 자체로는 parallelStream()과 동작하지 않는다. 병렬 스트림을 통해 트레이싱tracing 정보를 전파하고 싶을 땐, 앞에서 보여준 대로 supplyAsync(...)를 통해 구현해야 한다.

Executor 인터페이스를 구현한 빈 중에 span을 만들고 싶지 않은 게 있다면, spring.sleuth.async.ignored-beans 프로퍼티에 해당 빈들의 이름을 지정하면 된다.

이 동작은 spring.sleuth.async.enabled 값을 false로 설정하면 비활성화할 수 있다.

Customization of Executors

간혹 AsyncExecutor의 커스텀 인스턴스를 설정해야 하는 경우가 있다. 커스텀 Executor를 설정하는 방법은 아래 예제를 참고해라:

@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EnableAsync
// add the infrastructure role to ensure that the bean gets auto-proxied
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static class CustomExecutorConfig extends AsyncConfigurerSupport {

    @Autowired
    BeanFactory beanFactory;

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // CUSTOMIZE HERE
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        // DON'T FORGET TO INITIALIZE
        executor.initialize();
        return new LazyTraceExecutor(this.beanFactory, executor);
    }

}

이 설정을 빈 후처리post process 중에 처리할 수 있도록, @Configuration 클래스에 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)를 추가하는 것을 잊지 말자.


6.3. HTTP Client Integration

이 섹션에서 설명하는 기능들은 spring.sleuth.web.client.enabled 프로퍼티를 false와 같이 설정해 비활성화할 수 있다.

6.3.1. Synchronous Rest Template

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 RestTemplate 인터셉터를 주입해 요청에 모든 트레이싱tracing 정보가 전달되도록 만든다. 호출이 이루어질 때마다 새 span이 생성되고, 이 span은 응답을 받자마자 닫힌다. 동기식synchronous RestTemplate 기능들을 끄려면 spring.sleuth.web.client.enabledfalse로 설정해라.

인터셉터를 주입할 수 있으려면 RestTemplate을 빈으로 등록해야 한다. new 키워드로 RestTemplate 인스턴스를 직접 생성했다면 요청을 계측instrumentation하지 않는다.

6.3.2. Asynchronous Rest Template

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth 2.0.0 버전부터는 더 이상 AsyncRestTemplate 타입 빈을 등록하지 않는다. AsyncRestTemplate 빈이 필요하다면 직접 만들어야 한다. 그러면 xsleuth에서 알아서 요청을 계측instrumentation해줄 거다.

AsyncRestTemplate 관련 기능들을 끄려면 spring.sleuth.web.async.client.enabledfalse로 설정해라. 디폴트 TraceAsyncClientHttpRequestFactoryWrapper를 생성하지 않으려면 spring.sleuth.web.async.client.factory.enabledfalse로 설정해라. AsyncRestClient를 아예 생성하지 않으려면 spring.sleuth.web.async.client.template.enabledfalse로 설정해라.

Multiple Asynchronous Rest Templates

간혹 비동기asynchronous 비동기 RestTemplate의 구현체가 여러 개 필요한 경우가 있다. 다음은 이럴 때 필요한 커스텀 AsyncRestTemplate을 설정하는 예시다:

@Configuration(proxyBeanMethods = false)
public static class TestConfig {

    @Bean(name = "customAsyncRestTemplate")
    public AsyncRestTemplate traceAsyncRestTemplate() {
        return new AsyncRestTemplate(asyncClientFactory(), clientHttpRequestFactory());
    }

    private ClientHttpRequestFactory clientHttpRequestFactory() {
        ClientHttpRequestFactory clientHttpRequestFactory = new CustomClientHttpRequestFactory();
        // CUSTOMIZE HERE
        return clientHttpRequestFactory;
    }

    private AsyncClientHttpRequestFactory asyncClientFactory() {
        AsyncClientHttpRequestFactory factory = new CustomAsyncClientHttpRequestFactory();
        // CUSTOMIZE HERE
        return factory;
    }

}

Troubleshooting Async Configuration Issues

Sleuth는 빈 포스트 프로세서Bean Post Processor (BPP)를 사용해 Executor를 수정하기 때문에, 설정한 Executor 타입 대신 트레이스trace를 지원하는 sleuth의 executor 타입이 반환될 수 있다. 이로 인해 다음과 유사한 예외가 발생하기도 한다.

12:12:49.606 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2401f4c3, started on Wed Jan 19 12:12:49 GMT 2022
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'exampleConfigurer' is expected to be of type 'com.example.demo.Gh29151Application$Example' but was actually of type 'org.springframework.cloud.sleuth.instrument.async.LazyTraceAsyncCustomizer'

이러한 예외를 만났을 때 해결책은 다음과 같다:

예제

spring.sleuth.async.enabled=false

configuration 예제

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

    private final BeanFactory beanFactory;

    public AsyncConfiguration(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    @Bean("AsyncTaskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.initialize();
        return new LazyTraceThreadPoolTaskExecutor(beanFactory, executor);
    }
}

더 자세한 정보는 이 이슈에서 확인할 수 있다.

WebClient

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 ExchangeFilterFunction 구현체를 주입하는데, 이 클래스는 span을 생성하고, span을 잘 관리하고 있다가 on-success/on-error 콜백을 통해 닫아준다.

이 기능을 끄려면 spring.sleuth.web.client.enabled 프로퍼티를 false로 설정해라.

트레이싱 정보들을 계측instrumentation하려면 WebClient를 빈으로 등록해줘야 한다. new 키워드로 WebClient 인스턴스를 직접 생성하면 아무것도 계측instrumentation되지 않는다.

Logbook with WebClient

WebClient org.zalando:logbook-spring-boot-webflux-autoconfigure로 Logbook에 대한 지원을 추가하려면 아래 설정을 추가해야 한다. 이 통합 기능에 대한 자세한 내용은 이 이슈에서 확인할 수 있다.

@Configuration
@Import(LogbookWebFluxAutoConfiguration.class)
public class LogbookConfiguration {

    @Bean
    public LogstashLogbackSink logbackSink(final HttpLogFormatter formatter) {
        return new LogstashLogbackSink(formatter);
    }

    @Bean
    public CorrelationId correlationId(final Tracer tracer) {
        return request -> requireNonNull(requireNonNull(tracer.currentSpan())).context().traceId();
    }

    @Bean
    ReactorNettyHttpTracing reactorNettyHttpTracing(final HttpTracing httpTracing) {
        return ReactorNettyHttpTracing.create(httpTracing);
    }

    @Bean
    NettyServerCustomizer nettyServerCustomizer(final Logbook logbook,
            final ReactorNettyHttpTracing reactorNettyHttpTracing) {
        return server -> reactorNettyHttpTracing.decorateHttpServer(
                server.doOnConnection(conn -> conn.addHandlerFirst(new LogbookServerHandler(logbook))));
    }

    @Bean
    WebClient webClient(final Logbook logbook, final ReactorNettyHttpTracing reactorNettyHttpTracing) {
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(reactorNettyHttpTracing.decorateHttpClient(HttpClient
                        .create().doOnConnected(conn -> conn.addHandlerLast(new LogbookClientHandler(logbook))))))
                .build();
    }

}

Traverson

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Traverson 라이브러리를 사용하는 경우, RestTemplate 빈을 가져와 Traverson 객체에 주입해줄 수 있다. 이 RestTemplate은 이미 인터셉터가 설정돼 있기 때문에, RestTemplate을 사용할 때와 동일하게 그대로 클라이언트를 추적할 수 있다. 다음은 RestTemplate을 주입받는 방법을 보여주는 슈도 코드다:

@Autowired RestTemplate restTemplate;

Traverson traverson = new Traverson(URI.create("https://some/address"),
    MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8).setRestOperations(restTemplate);
// use Traverson

Apache HttpClientBuilder and HttpAsyncClientBuilder

이 기능은 Brave 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 HttpClientBuilderHttpAsyncClientBuilder를 계측instrumentation하기 때문에, 요청이 들어오면 트레이싱tracing 컨텍스트가 주입된다.

이 기능을 끄려면 spring.sleuth.web.client.enabledfalse로 설정해라.

Netty HttpClient

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Netty의 HttpClient를 계측instrumentation한다.

이 기능을 끄려면 spring.sleuth.web.client.enabledfalse로 설정해라.

요청을 계측instrumentation하려면 HttpClient를 빈으로 등록해야 한다. new 키워드를 사용해 HttpClient 인스턴스를 직접 생성하면 아무것도 계측instrumentation되지 않는다.

UserInfoRestTemplateCustomizer

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Spring Security의 UserInfoRestTemplateCustomizer를 계측한다.

이 기능을 끄려면 spring.sleuth.web.client.enabledfalse로 설정해라.


6.4. HTTP Server Integration

이 섹션에서 설명하는 기능들은 spring.sleuth.web.enabled 프로퍼티를 false와 같이 설정하면 비활성화할 수 있다.

6.4.1. HTTP Filter

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

들어온 요청 중 샘플링하는 모든 요청은 TracingFilter를 통해 Span을 생성한다. spring.sleuth.web.skipPattern 프로퍼티를 설정하면 건너뛸 URI를 지정할 수 있다. 클래스패스에 ManagementServerProperties가 있는 경우, 지정한 skip 패턴에 contextPath의 값도 추가된다. Sleuth의 디폴트 skip 패턴은 그대로 사용하면서 별도 패턴을 추가로 넣고 싶다면, spring.sleuth.web.additionalSkipPattern으로 원하는 패턴을 전달해라.

기본적으로 스프링 부트 액추에이터 엔드포인트들은 전부 skip 패턴에 자동으로 추가된다. 이 동작을 원치 않는다면 spring.sleuth.web.ignore-auto-configured-skip-patternstrue로 설정해라.

TracingFilter를 등록하는 순서를 변경하려면, spring.sleuth.web.filter-order 프로퍼티를 수정해라.

처리되지 않은 예외uncaught exception를 로깅하는 필터를 비활성화하려면, spring.sleuth.web.exception-throwing-filter-enabled 프로퍼티를 비활성화하면 된다.

6.4.2. Async Servlet support

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

컨트롤러가 Callable이나 WebAsyncTask를 반환하는 경우 Spring Cloud Sleuth는 span을 새로 생성하지 않고 기존 span을 계속 이어간다.

6.4.3. WebFlux support

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

들어온 요청 중 샘플링하는 모든 요청은 TraceWebFilter를 통해 Span을 생성한다. 이때 Span의 이름은 http: + 요청이 전송된 경로를 사용한다. 예를 들어 요청이 /this/that으로 전송된 경우에 Span의 이름은 http:/this/that이다. spring.sleuth.web.skipPattern 프로퍼티를 설정하면 건너뛸 URI를 지정할 수 있다. 클래스패스에 ManagementServerProperties가 있는 경우, 지정한 skip 패턴에 contextPath의 값도 추가된다. Sleuth의 디폴트 skip 패턴은 그대로 사용하면서 별도 패턴을 추가로 넣고 싶다면, spring.sleuth.web.additionalSkipPattern으로 원하는 패턴을 전달해라.

성능이나 컨텍스트 전파propagation 측면에서 효율을 끌어올리려면, spring.sleuth.reactor.instrumentation-typeMANUAL로 변경하는 것이 좋다. 스코프 내에 있는 span으로 코드를 실행하려면 WebFluxSleuthOperators.withSpanInScope를 호출하면 된다. 예를 들어:

@GetMapping("/simpleManual")
public Mono<String> simpleManual() {
    return Mono.just("hello").map(String::toUpperCase).doOnEach(WebFluxSleuthOperators
            .withSpanInScope(SignalType.ON_NEXT, signal -> log.info("Hello from simple [{}]", signal.get())));
}

트레이싱tracing 필터를 등록하는 순서를 변경하려면, spring.sleuth.web.filter-order 프로퍼티를 수정해라.

6.4.4. Reactor Netty HttpServer

Reactor Netty를 사용 중인데 액세스 로그를 계측instrumentation하고 싶다면, io.projectreactor.netty:reactor-netty-http-brave를 추가해야 한다 (Brave Tracer에서만 동작한다). 또한 프로젝트에 아래 설정을 추가해라:

@Configuration(proxyBeanMethods = false)
class TraceNettyConfig {

    @Bean
    NettyServerCustomizer traceNettyServerCustomizer(ObjectProvider<HttpTracing> tracing) {
        return server -> ReactorNettyHttpTracing.create(tracing.getObject()).decorateHttpServer(server);
    }
}

6.5. Messaging

이 섹션에서 설명하는 기능들은 spring.sleuth.messaging.enabled 프로퍼티를 false와 같이 설정하면 비활성화할 수 있다.

6.5.1. Spring Integration

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Spring Cloud Sleuth는 Spring Integration과 통합된다. 즉, 이벤트를 발행publish하고 구독subscribe할 때마다 span을 생성한다. Spring Integration 계측instrumentation 기능을 비활성화하려면, spring.sleuth.integration.enabledfalse로 설정해라.

트레이싱tracing에 포함시킬 채널명을 직접 명시하고 싶다면, spring.sleuth.integration.patterns에 원하는 패턴을 제공하면 된다. 기본적으로는 hystrixStreamOutput 채널을 제외한 모든 채널을 추적한다.

Executor를 사용해 Spring Integration의 IntegrationFlow을 빌드하는 경우, 트레이싱tracing 관련 코드가 없는 본래 Executor를 사용해야 한다. TraceableExecutorService로 Spring Integration Executor 채널을 감싸게 되면decorate, span을 닫는 동작과 관련해서 에러가 발생한다.

메시지 헤더에서 트레이싱tracing 컨텍스트를 읽고 쓰는 방식을 커스텀하고 싶다면, 아래 타입으로 빈을 등록해주기만 하면 된다:

Spring Integration Customization

Customizing messaging spans

span의 디폴트 이름과 태그를 변경하려면 MessageSpanCustomizer 타입 빈만 등록해주면 된다. 아니면 기존 DefaultMessageSpanCustomizer를 상속해 기존 동작을 재정의하는 방법도 있다.

@Component
  class MyMessageSpanCustomizer extends DefaultMessageSpanCustomizer {
      @Override
      public Span customizeHandle(Span spanCustomizer,
              Message<?> message, MessageChannel messageChannel) {
          return super.customizeHandle(spanCustomizer, message, messageChannel)
                  .name("changedHandle")
                  .tag("handleKey", "handleValue")
                  .tag("channelName", channelName(messageChannel));
      }

      @Override
      public Span.Builder customizeSend(Span.Builder builder,
              Message<?> message, MessageChannel messageChannel) {
          return super.customizeSend(builder, message, messageChannel)
                  .name("changedSend")
                  .tag("sendKey", "sendValue")
                  .tag("channelName", channelName(messageChannel));
      }
  }

6.5.2. Spring Cloud Function and Spring Cloud Stream

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Spring Cloud Sleuth는 Spring Cloud Function를 계측instrumentation할 수 있다. Spring Cloud Stream은 Spring Cloud Function을 사용하기 때문에, 특별한 설정 없이도 메시지 처리를 바로 계측instrumentation할 수 있다.

이땐 파라미터로 Message를 받는 Function이나 Consumer, 또는 Supplier를 제공하면 된다 (e.g. Function<Message<String>, Message<Integer>>). Message 타입을 받지 않는 경우 메시지 처리 동작을 계측instrumentation하지 않는다.

Consumer<Flux<Message<?>>>처럼 리액티브 타입을 사용한다면, .subscribe()를 호출하기 전에 span을 수동으로 닫고 컨텍스트를 비워야 한다는 점을 기억해두자. 예를 들어:

@Bean
    Consumer<Flux<Message<String>>> channel(Tracer tracer) {
        // For the reactive consumer remember to call "subscribe()" at the end, otherwise
        // you'll get the "Dispatcher has no subscribers" error
        return i -> i
                    .doOnNext(s -> log.info("HELLO"))
                    // You must finish the span yourself and clear the tracing context like presented below.
                    // Otherwise you will be missing out the span that wraps the function execution.
                    .doOnNext(s -> {
                        tracer.currentSpan().end();
                        tracer.withSpan(null);
                    })
                    .subscribe();
    }
}

NOTE: Sleuth가 어떤 Supplier와도 함께 동작할 수 있으려면 (e.g. Supplier<Flux<Message<String>>>), spring.sleuth.integration.enabledtrue로 설정해 Spring Integration 기반 계측instrumentation으로 폴백해야 한다.

Spring Cloud Stream 통합 기능은 spring.sleuth.function.enabledfalse로 설정해 비활성화할 수 있다.

Spring Cloud Stream의 리액티브 메시징 컨텍스트 내에서 span의 수명 주기를 전부 다 제어하고 싶다면, Spring Cloud Stream 통합을 비활성화하고 유틸리티 클래스 MessagingSleuthOperators를 활용해야 한다는 점을 기억해두자. MessagingSleuthOperators를 사용하면 입출력 메시지를 조작해서, 트레이싱tracing 컨텍스트를 계속 이어가고 트레이싱tracing 컨텍스트 내에서 사용자 정의 코드를 실행할 수 있다.

class SimpleReactiveManualFunction implements Function<Flux<Message<String>>, Flux<Message<String>>> {

    private static final Logger log = LoggerFactory.getLogger(SimpleReactiveFunction.class);

    private final BeanFactory beanFactory;

    SimpleReactiveManualFunction(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public Flux<Message<String>> apply(Flux<Message<String>> input) {
        return input.map(message -> (MessagingSleuthOperators.asFunction(this.beanFactory, message))
                .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.beanFactory, msg, stringMessage -> {
                    log.info("Hello from simple manual [{}]", stringMessage.getPayload());
                    return stringMessage;
                })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.beanFactory, msg, null))
                .andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders()))
                .andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.beanFactory, msg)).apply(message));
    }

}

6.5.3. Spring RabbitMq

이 기능은 Brave 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 RabbitTemplate을 계측instrumentation해서 메시지에 트레이싱tracing 헤더를 주입한다.

이 기능을 끄러면 spring.sleuth.messaging.rabbit.enabledfalse로 설정해라.

6.5.4. Spring Kafka

이 기능은 Brave 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Spring Kafka의 ProducerFactoryConsumerFactory를 계측instrumentation해서 기존 Spring Kafka의 ProducerConsumer에 트레이싱tracing 헤더를 주입해준다.

이 기능을 그러면 spring.sleuth.messaging.kafka.enabledfalse로 설정해라.

6.5.5. Spring Kafka Streams

이 기능은 Brave 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 KafkaStreams KafkaClientSupplier를 계측instrumentation해서 ProducerConsumer들에 트레이싱tracing 헤더를 주입해준다. KafkaStreamsTracing 빈은 TransformerSupplierProcessorSupplier 메소드들을 통해 다른 것들을 추가적으로 계측instrumentation해준다.

이 기능을 끄러면 spring.sleuth.messaging.kafka.streams.enabledfalse로 설정해라.

6.5.6. Spring JMS

이 기능은 Brave 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 JmsTemplate을 계측instrumentation해서 메시지에 트레이싱tracing 헤더를 주입한다. 또한 컨슈머 측에서 메소드 위에 @JmsListener 애노테이션을 선언한 코드도 지원한다.

이 기능을 끄러면 spring.sleuth.messaging.jms.enabledfalse로 설정해라.

JMS에서 baggage 전파propagation는 지원하지 않는다.


6.6. OpenFeign

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

기본적으로 Spring Cloud Sleuth는 TraceFeignClientAutoConfiguration을 통해 Feign과의 통합을 지원한다. spring.sleuth.feign.enabledfalse로 설정하면 이 기능을 완전히 비활성화할 수 있다. 이렇게 설정하면 Feign 관련 계측instrumentation 로직은 실행되지 않는다.

Feign 계측instrumentation 동작 중 일부는 FeignBeanPostProcessor를 통해 수행된다. 이 동작은 spring.sleuth.feign.processor.enabledfalse로 설정면 비활성화할 수 있다. 이 프로퍼티를 false로 설정하면, Spring Cloud Sleuth는 커스텀 Feign 컴포넌트들은 계측instrumentation하지 않는다. 하지만 다른 디폴트 계측instrumentation 동작들은 전부 그대로 유지된다.


6.7. OpenTracing

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Spring Cloud Sleuth는 OpenTracing과 호환된다. 클래스패스에 OpenTracing이 존재하면, OpenTracing Tracer 빈을 자동으로 등록한다. 이 기능을 비활성화하려면 spring.sleuth.opentracing.enabledfalse로 설정해라.


6.8. Quartz

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Quartz 스케줄러에 Job/Trigger 리스너를 추가하는 식으로 quartz job을 계측instrumentation한다.

이 기능을 끄려면 spring.sleuth.quartz.enabled 프로퍼티를 false로 설정해라.


6.9. Reactor

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 다음과 같은 모드로 리액터 기반 애플리케이션을 계측instrumentation할 수 있으며, 이 모드들은 spring.sleuth.reactor.instrumentation-type 프로퍼티를 통해 변경할 수 있다:

이전 버전과의 호환성 때문에 현재 기본값은 ON_EACH이지만, MANUAL 계측instrumentation으로 마이그레이션해서 WebFluxSleuthOperatorsMessagingSleuthOperators를 최대한 활용하기를 권장한다. 성능이 상당히 개선될 거다. 예를 들어:

@GetMapping("/simpleManual")
public Mono<String> simpleManual() {
    return Mono.just("hello").map(String::toUpperCase).doOnEach(WebFluxSleuthOperators
            .withSpanInScope(SignalType.ON_NEXT, signal -> log.info("Hello from simple [{}]", signal.get())));
}

리액터 지원을 비활성화하려면 spring.sleuth.reactor.enabled 프로퍼티를 false로 설정해라.


6.10. Redis

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Lettuce의 Tracing 코드를 사용한다. 클래스패스에 Brave가 있는 경우 TracingBraveTracing으로 설정한다.

Redis 지원을 비활성화하려면 spring.sleuth.redis.enabled 프로퍼티를 false로 설정해라.

6.10.1. Redis With Legacy Brave Only Support

Brave만 지원하는 기능을 사용하려면 spring.sleuth.redis.legacy.enabled의 값을 true로 설정해야 한다. 이 설정은 Spring Cloud Sleuth 3.1.0 버전까지 제공되는 디폴트 메커니즘이다.

Sleuth에선 tracing 프로퍼티를 Lettuce ClientResources 인스턴스로 설정해 Lettuce에 내장된 Brave 트레이싱tracing 기능을 활성화한다.

Spring Cloud Sleuth는 트레이스trace를 지원하는 ClientResources 빈을 제공한다. 자체 구현체를 빈으로 사용하고 싶은 경우, 아래 예시와 같이 ClientResourcesBuilderCustomizer의 스트림으로 ClientResources.Builder를 커스텀하는 것을 잊지 말자:

@Bean(destroyMethod = "shutdown")
DefaultClientResources myLettuceClientResources(ObjectProvider<ClientResourcesBuilderCustomizer> customizer) {
    DefaultClientResources.Builder builder = DefaultClientResources.builder();
    // setting up the builder manually
    customizer.stream().forEach(c -> c.customize(builder));
    return builder.build();
}

6.11. Runnable and Callable

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

특정 로직을 Runnable이나 Callable로 감싸는 경우, Sleuth가 제공하는 전용 클래스로 한 번 더 감싸면 된다. 다음은 Runnable을 사용하는 예시이다:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        // do some work
    }

    @Override
    public String toString() {
        return "spanNameFromToStringMethod";
    }
};
// Manual `TraceRunnable` creation with explicit "calculateTax" Span name
Runnable traceRunnable = new TraceRunnable(this.tracer, spanNamer, runnable, "calculateTax");

다음은 Callable 사용 예시다:

Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
        return someLogic();
    }

    @Override
    public String toString() {
        return "spanNameFromToStringMethod";
    }
};
// Manual `TraceCallable` creation with explicit "calculateTax" Span name
Callable<String> traceCallable = new TraceCallable<>(tracer, spanNamer, callable, "calculateTax");

이렇게 코드를 작성하면 로직이 실행될 때마다 새 span이 생성되고 닫힌다.


6.12. RPC

이 기능은 Brave 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 gRPC나 Dubbo와 같은 RPC 계측instrumentation의 기반이 되는 RpcTracing 빈을 자동으로 설정해준다.

RPC를 추적할 때 사용할 클라이언트/서버 샘플링 로직을 커스텀해야 하는 경우, brave.sampler.SamplerFunction<RpcRequest> 타입 빈을 등록하고, 클라이언트 샘플러의 경우 빈 이름을 sleuthRpcClientSampler로, 서버 샘플러의 경우 sleuthRpcServerSampler로 지정하기만 하면 된다.

간단히 빈을 주입받 싶으면 @RpcClientSampler@RpcServerSampler 애노테이션을 사용하면 되고, 빈의 이름은 각 애노테이션에 정의된 스태틱 문자열 NAME 필드를 참조하면 된다.

Ex. 다음은 초당 100개의 서버 요청 “GetUserToken”을 추적하는 샘플러다. 이때 health check 서비스에 대한 요청은 트레이스trace를 새로 시작하지 않는다. 다른 요청은 글로벌 샘플링 설정에 따른다.

@Configuration(proxyBeanMethods = false)
    class Config {
  @Bean(name = RpcServerSampler.NAME)
  SamplerFunction<RpcRequest> myRpcSampler() {
      Matcher<RpcRequest> userAuth = and(serviceEquals("users.UserService"), methodEquals("GetUserToken"));
      return RpcRuleSampler.newBuilder().putRule(serviceEquals("grpc.health.v1.Health"), Sampler.NEVER_SAMPLE)
              .putRule(userAuth, RateLimitingSampler.create(100)).build();
  }
}

더 자세한 내용은 github.com/openzipkin/brave/tree/master/instrumentation/rpc#sampling-policy를 참고해라.

6.12.1. Dubbo RPC support

Spring Cloud Sleuth는 Brave와의 통합을 통해 Dubbo를 지원한다. brave-instrumentation-dubbo 의존성만 추가하면 된다:

<dependency>
    <groupId>io.zipkin.brave</groupId>
    <artifactId>brave-instrumentation-dubbo</artifactId>
</dependency>

또한 dubbo.properties 파일에 다음과 같이 설정을 추가해야 한다:

dubbo.provider.filter=tracing
dubbo.consumer.filter=tracing

Brave - Dubbo 통합에 관한 자세한 내용은 이곳에서 확인할 수 있다. Spring Cloud Sleuth와 Dubbo를 사용하는 예제는 여기에서 확인할 수 있다.

6.12.2. gRPC

Spring Cloud Sleuth는 Brave 트레이서tracer를 통해 gRPC 계측instrumentation 기능을 제공한다. spring.sleuth.grpc.enabledfalse로 설정하면 이 기능을 완전히 비활성화할 수 있다.

Variant 1

의존성

gRPC 통합에선 클라이언트와 서버 계측instrumentation을 위해 두 가지 외부 라이브러리에 의존하며, 두 라이브러리가 모두 클래스패스에 있어야 계측instrumentation 기능이 활성화된다.

Maven:

        <dependency>
            <groupId>io.github.lognet</groupId>
            <artifactId>grpc-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-grpc</artifactId>
        </dependency>

Gradle:

    compile("io.github.lognet:grpc-spring-boot-starter")
    compile("io.zipkin.brave:brave-instrumentation-grpc")
서버 계측

Spring Cloud Sleuth는 grpc-spring-boot-starter를 활용해 @GRpcService를 선언한 모든 서비스와 Brave의 gRPC 서버 인터셉터를 등록한다.

클라이언트 계측

gRPC 클라이언트는 ManagedChannelBuilder를 활용해 gRPC 서버와 통신할 때 사용하는 ManagedChannel을 구성한다. 네이티브 ManagedChannelBuilderManagedChannel 인스턴스를 구성할 때 진입점으로 사용할 수 있는 스태틱 메소드를 제공하지만, 이 메커니즘은 스프링 애플리케이션 컨텍스트의 영향 밖에 있다.

Spring Cloud Sleuth는 SpringAwareManagedChannelBuilder를 제공하는데, 이 빌더는 스프링 애플리케이션 컨텍스트를 통해 커스텀할 수 있고, gRPC 클라이언트로 주입받을 수 있다. ManagedChannel 인스턴스를 생성할 때는 반드시 이 빌더를 사용해야 한다.

Sleuth는 TracingManagedChannelBuilderCustomizer를 생성해서 Brave의 클라이언트 인터셉터를 SpringAwareManagedChannelBuilder에 주입한다.

Variant 2

Grpc 스프링 부트 스타터는 gRPC를 위한 Spring Cloud Sleuth와 Brave의 계측instrumentation 코드가 있는지 자동으로 감지하고 필요한 클라이언트나 서버 툴을 등록한다.


6.13. RxJava

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 모든 Action0 인스턴스를 TraceAction이라는 Sleuth의 클래스로 래핑하는 커스텀 RxJavaSchedulersHook을 등록한다. 이 훅은 Action을 스케줄링하기 전에 트레이싱tracing이 이미 진행 중인지 여부에 따라 span을 새로 시작하거나 계속 이어간다. 이 커스텀 RxJavaSchedulersHook을 비활성화하려면 spring.sleuth.rxjava.schedulers.hook.enabledfalse로 설정해라.

Span을 새로 생성하지 않을 스레드의 이름은 정규식으로 정의할 수 있다. spring.sleuth.rxjava.schedulers.ignoredthreads 프로퍼티에 정규식 목록을 콤마로 구분해서 지정하면 된다.

리액티브 프로그래밍과 Sleuth를 함께 사용할 때는 리액터의 기능을 활용하는 것을 권장한다.


6.14. Spring Cloud CircuitBreaker

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에 Spring Cloud CircuitBreaker가 있다면, sleuth는 전달받은 command Supplier와 fallback Function을 전용 클래스로 래핑한다. 또한 CircuitBreaker의 리액티브 구현체 역시 계측instrumentation을 지원한다. 이 기능을 비활성화하려면 spring.sleuth.circuitbreaker.enabledfalse로 설정해라.


6.15. Spring Cloud Config Server

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에서 Spring Cloud Config 서버가 실행 중이면, sleuth는 EnvironmentRepository를 span으로 감싼다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.config.server.enabledfalse로 설정해라.


6.16. Spring Cloud Deployer

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에서 Spring Cloud Deployer가 실행 중이면, sleuth는 AppDeployer를 전용 클래스로 래핑한다. 이 클래스에선 애플리케이션의 상태를 디폴트 인터벌에 따라 폴링한다. 기본값은 spring.sleuth.deployer.status-poll-delay 프로퍼티를 통해 변경할 수 있다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.deployer.enabledfalse로 설정해라.


6.17. Spring RSocket

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에서 Spring RSocket이 실행 중이면, sleuth는 인바운드/아웃바운드 통신을 감싸 메타데이터를 통해 트레이싱tracing 컨텍스트를 전파propagate한다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.rsocket.enabledfalse로 설정해라.


6.18. Spring Batch

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에서 Spring Batch가 실행 중인 경우, sleuth는 StepBuilderFactoryJobBuilderFactory를 래핑해서 트레이싱tracing 컨텍스트를 전파propagate한다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.batch.enabledfalse로 설정해라.


6.19. Spring Cloud Task

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에서 Spring Cloud Task가 실행 중인 경우, sleuth는 TaskExecutionListenerCommandLineRunner, ApplicationRunner를 계측instrumentation한다. 이 기능을 비활성화하려면 spring.sleuth.task.enabledfalse로 설정해라.


6.20. Spring Tx

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에 Spring Tx가 있다면, sleuth는 PlatformTransactionManagerReactiveTransactionManager를 계측instrumentation해서 새 트랜잭션이 생성될 때마다 span을 새로 만든다. 기술적인 한계로 인해 스프링의 AbstractPlatformTransactionManager를 상속한 클래스들은 계측instrumentation하지 않는다. 이 기능을 비활성화하려면 spring.sleuth.tx.enabledfalse로 설정해라.


6.21. Spring Security

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에 Spring Security가 있다면, sleuth는 컨텍스트가 변경될 때 현재 스팬에 이벤트를 추가하는 SecurityContextChangedListener의 구현체를 생성한다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.security.enabledfalse로 설정해라.


6.22. R2DBC

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

클래스패스에 R2DBC 프록시가 있다면, sleuth는 ConnectionFactory를 계측instrumentation해서 커스텀 ProxyExecutionListener를 하나 추가한다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.r2dbc.enabledfalse로 설정해라.


6.23. Spring Vault

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Spring Vault가 Vault와 통신하는 데 사용하는 RestTemplate이나 WebClient 인스턴스를 계측instrumentation하고 있다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.vault.enabledfalse로 설정해라.


6.24. Spring Tomcat

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Tomcat의 Valve를, span을 사용하는 전용 구현체로 하나 추가한다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.web.tomcat.enabledfalse로 설정해라.


6.25. Spring Data Cassandra

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Casandra의 CqlSessionReactiveSession 인터페이스를 계측instrumentation하고 있으며, RequestTracker의 자체 구현체를 제공하고 있다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.cassandra.enabledfalse로 설정해라.


6.26. Spring JDBC

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다. 관련 코드들은 spring-boot-datasource-decorator 프로젝트에서 가져왔다.

Sleuth는 DataSource를 전용 클래스로 감싼다decorate. 실제 프록시 처리는 p6spy 혹은 datasource-proxy에 위임한다. 이 기능을 사용하려면 해당 프록시가 클래스패스에 있어야 한다.

P6Spy Maven P6Spy Gradle Datasource Proxy Maven Datasource Proxy Gradle
<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>${p6spy.version}</version>
    <scope>runtime</scope>
</dependency>
runtimeOnly "p6spy:p6spy:${p6spyVersion}"
<dependency>
    <groupId>net.ttddyy</groupId>
    <artifactId>datasource-proxy</artifactId>
    <version>${datasource-proxy.version}</version>
    <scope>runtime</scope>
</dependency>
runtimeOnly "net.ttddyy:datasource-proxy:${datasourceProxyVersion}"

전체 p6spy 설정 옵션들은 부록 페이지에서 spring.sleuth.jdbc.p6spy를, 전체 데이터소스 프록시 설정 옵션들은 spring.sleuth.jdbc.datasource-proxy를 확인하길 바란다.

P6Spy의 경우 기본적으로 파라미터 값들은 로깅하지 않으므로, 파라미터도 로그에 남기려면 spring.sleuth.jdbc.p6spy.tracing.include-parameter-valuestrue로 설정해라.

P6Spy가 제공하는 설정 메소드 중 하나를 사용해 P6Spy를 수동으로 구성할 수도 있다. 자세한 내용은 P6Spy 설정 가이드를 참고해라.

Datasource Proxy의 경우 기본적으로 쿼리를 로깅하지 않으므로, 슬로우 쿼리를 로그에 남기려면 spring.sleuth.jdbc.datasource-proxy.slow-query.enable-loggingtrue로 설정하고, 모든 쿼리를 로그에 남기려면 spring.sleuth.jdbc.datasource-proxy.query.enable-loggingtrue로 설정해라.

이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.jdbc.enabledfalse로 설정해라.


6.27. MongoDB

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 모든 명령어를 span으로 감싸는 커맨드 리스너를 추가한다. Span에 별도로 소켓 주소 관련 태그를 추가하려면 spring.sleuth.mongodb.socket-address-span-customizer.enabledtrue로 설정해라.

이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.mongodb.enabledfalse로 설정해라.


6.28. Spring Session

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 모든 작업operation을 span으로 감싸는 Session 레포지토리를 계측instrumentation하고 있다. 이 계측instrumentation 기능을 비활성화하려면 spring.sleuth.session.enabledfalse로 설정해라.


6.29. Kotlin Coroutines

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

Sleuth는 Tracer 빈을 통해 현재 span을 조회할 수 있게 해주는 코틀린 코루틴을 추가하고 있다. Tracer.asContextElement() 메소드를 실행해 Tracer 빈을 코틀린 코루틴 컨텍스트로 전달해주면 되고, 아니면 클래스패스에 Reactor Kotlin 코루틴 통합이 들어있는 경우엔 Reactor의 컨텍스트에서 Tracer 빈을 가져올 수도 있다. 현재 span을 조회하려면 코틀린 코루틴 내에서 currentSpan() 메소드를 호출하면 된다.


6.30. Prometheus Exemplars

이 기능은 모든 트레이서tracer 구현체에서 사용할 수 있다.

프로메테우스 ExemplarSpanContextSupplier를 통해 지원한다. Micrometer를 사용 중이라면 자동으로 설정되지만, 원한다면 프로메테우스에 직접 SpanContextSupplier를 등록할 수도 있다.
이 기능은 프로메테우스 쪽에서 명시적으로 활성화해줘야 하며, OpenMetrics 형식에서만 지원되므로 프로메테우스 문서를 확인해보기 바란다.


Next :
Appendix
Spring Cloud Sleuth에서 사용하는 애플리케이션 프로퍼티들과, Spring Cloud Sleuth가 생성하는 span들 정리

전체 목차는 여기에 있습니다.

<< >>

TOP