Loading...
Spring Framework Reference Documentation 7.0.2의 Observability Support의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Micrometer는 애플리케이션에서 Metrics와 Traces를 모두 활성화하는 Observation 개념을 정의합니다. Metrics 지원은 애플리케이션의 런타임 동작에 대한 통계를 수집하기 위해 타이머, 게이지 또는 카운터를 생성하는 방법을 제공합니다. Metrics는 오류율, 사용 패턴, 성능 등을 추적하는 데 도움을 줄 수 있습니다.
Traces는 애플리케이션 경계를 가로지르며 전체 시스템에 대한 전체적인 뷰를 제공합니다. 특정 사용자 요청을 확대해서 해당 요청의 전체 완료 과정을 애플리케이션 간에 따라갈 수 있습니다.
Spring Framework는 ObservationRegistry가 구성된 경우 자체 코드베이스의 다양한 부분에 인스트루멘테이션을 적용하여 observation을 발행합니다.
Spring Boot에서 observability 인프라 구성에 대해 더 자세히 알아볼 수 있습니다.
Spring Framework는 observability를 위해 다양한 기능에 인스트루멘테이션을 적용합니다. 이 섹션의 시작 부분에서 설명한 것처럼, observation은 설정에 따라 타이머 Metrics 및/또는 Traces를 생성할 수 있습니다.
| Observation name | Description |
|---|---|
"http.client.requests" | HTTP 클라이언트 exchange에 소요된 시간 |
"http.server.requests" | Framework 수준에서 HTTP 서버 exchange의 처리 시간 |
"jms.message.publish" | 메시지 프로듀서가 destination으로 JMS 메시지를 보내는 데 소요된 시간. |
"jms.message.process" | 메시지 컨슈머가 이전에 수신한 JMS 메시지의 처리 시간. |
"tasks.scheduled.execution" | @Scheduled 태스크 실행의 처리 시간 |
Table 1. Observations produced by Spring Framework
| Note |
|---|
| Observation은 Micrometer의 공식 네이밍 컨벤션을 사용하지만, Metrics 이름은 모니터링 시스템 백엔드에서 선호하는 형식 (Prometheus, Atlas, Graphite, InfluxDB…)으로 자동 변환됩니다. |
Micrometer Observation에 익숙하지 않다면, 알아두어야 할 개념에 대한 간단한 요약은 다음과 같습니다.
Observation은 애플리케이션에서 발생하는 어떤 일을 실제로 기록하는 것입니다. 이는 metrics 또는 traces를 생성하기 위해 ObservationHandler 구현에 의해 처리됩니다.ObservationContext 구현이 있습니다. 이 타입은 그에 대한 메타데이터를 추출하기 위한 모든 관련 정보를 보유합니다.
HTTP 서버 observation의 경우, context 구현은 HTTP 요청, HTTP 응답, 처리 중 발생한 예외 등을 보유할 수 있습니다.Observation은 KeyValues 메타데이터를 보유합니다. HTTP 서버 observation의 경우, 이는 HTTP 요청 메서드, HTTP 응답 상태 등일 수 있습니다.
이 메타데이터는 자신이 지원하는 ObservationContext 타입을 선언해야 하는 ObservationConvention 구현에 의해 제공됩니다.KeyValues는 KeyValue 튜플에 대해 가능한 값의 수가 낮고 제한된 경우 "low cardinality"라고 합니다 (HTTP 메서드가 좋은 예입니다).
Low cardinality 값은 metrics에만 제공됩니다. 반대로, "high cardinality" 값은 (예를 들어, HTTP 요청 URI) 무한하며 traces에만 제공됩니다.ObservationDocumentation은 특정 도메인의 모든 observation을 문서화하며, 예상되는 키 이름과 그 의미를 나열합니다.Global 설정 옵션은 ObservationRegistry#observationConfig() 수준에서 사용할 수 있습니다.
각 인스트루먼트된 컴포넌트는 두 가지 확장 포인트를 제공합니다:
ObservationRegistry 설정; 설정되지 않은 경우, observation은 기록되지 않으며 no-op이 됩니다.KeyValues를 변경하기 위한 custom ObservationConvention 제공.ServerHttpObservationFilter가 있는 Spring MVC "http.server.requests" metrics 인스트루멘테이션 예제를 살펴보겠습니다.
이 observation은 ServerRequestObservationConvention과 ServerRequestObservationContext를 사용합니다. custom convention은 Servlet 필터에서 구성할 수 있습니다.
Observation으로 생성되는 메타데이터를 사용자 정의하려면, 요구 사항에 맞게 DefaultServerRequestObservationConvention을 확장할 수 있습니다:
1import io.micrometer.common.KeyValue; 2import io.micrometer.common.KeyValues; 3 4import org.springframework.http.server.observation.DefaultServerRequestObservationConvention; 5import org.springframework.http.server.observation.ServerRequestObservationContext; 6 7public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention { 8 9 @Override 10 public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) { 11 // here, we just want to have an additional KeyValue to the observation, keeping the default values 12 return super.getLowCardinalityKeyValues(context).and(custom(context)); 13 } 14 15 private KeyValue custom(ServerRequestObservationContext context) { 16 return KeyValue.of("custom.method", context.getCarrier().getMethod()); 17 } 18 19}
완전한 제어가 필요하다면, 관심 있는 observation에 대한 전체 convention contract를 구현할 수 있습니다:
1import java.util.Locale; 2 3import io.micrometer.common.KeyValue; 4import io.micrometer.common.KeyValues; 5 6import org.springframework.http.server.observation.ServerHttpObservationDocumentation; 7import org.springframework.http.server.observation.ServerRequestObservationContext; 8import org.springframework.http.server.observation.ServerRequestObservationConvention; 9 10public class CustomServerRequestObservationConvention implements ServerRequestObservationConvention { 11 12 @Override 13 public String getName() { 14 // will be used as the metric name 15 return "http.server.requests"; 16 } 17 18 @Override 19 public String getContextualName(ServerRequestObservationContext context) { 20 // will be used for the trace name 21 return "http " + context.getCarrier().getMethod().toLowerCase(Locale.ROOT); 22 } 23 24 @Override 25 public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) { 26 return KeyValues.of(method(context), status(context), exception(context)); 27 } 28 29 @Override 30 public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) { 31 return KeyValues.of(httpUrl(context)); 32 } 33 34 private KeyValue method(ServerRequestObservationContext context) { 35 // You should reuse as much as possible the corresponding ObservationDocumentation for key names 36 return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.METHOD, context.getCarrier().getMethod()); 37 } 38 39 // status(), exception(), httpUrl()... 40 41 private KeyValue status(ServerRequestObservationContext context) { 42 return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, String.valueOf(context.getResponse().getStatus())); 43 } 44 45 private KeyValue exception(ServerRequestObservationContext context) { 46 String exception = (context.getError() != null ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE); 47 return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.EXCEPTION, exception); 48 } 49 50 private KeyValue httpUrl(ServerRequestObservationContext context) { 51 return KeyValue.of(ServerHttpObservationDocumentation.HighCardinalityKeyNames.HTTP_URL, context.getCarrier().getRequestURI()); 52 } 53 54}
Custom ObservationFilter를 사용하여 observation에 대한 키 값을 추가하거나 제거하는 방식으로도 유사한 목표를 달성할 수 있습니다.
Filter는 기본 convention을 대체하지 않으며 post-processing 컴포넌트로 사용됩니다.
1import io.micrometer.common.KeyValue; 2import io.micrometer.observation.Observation; 3import io.micrometer.observation.ObservationFilter; 4 5import org.springframework.http.server.observation.ServerRequestObservationContext; 6 7public class ServerRequestObservationFilter implements ObservationFilter { 8 9 @Override 10 public Observation.Context map(Observation.Context context) { 11 if (context instanceof ServerRequestObservationContext serverContext) { 12 context.setName("custom.observation.name"); 13 context.addLowCardinalityKeyValue(KeyValue.of("project", "spring")); 14 String customAttribute = (String) serverContext.getCarrier().getAttribute("customAttribute"); 15 context.addLowCardinalityKeyValue(KeyValue.of("custom.attribute", customAttribute)); 16 } 17 return context; 18 } 19}
ObservationFilter 인스턴스는 ObservationRegistry에 구성할 수 있습니다.
@Scheduled 태스크의 각 실행에 대해 Observation이 생성됩니다.
애플리케이션은 observation 기록을 활성화하기 위해 ScheduledTaskRegistrar에 ObservationRegistry를 구성해야 합니다.
이는 observation 레지스트리를 설정하는 SchedulingConfigurer 빈을 선언하여 수행할 수 있습니다:
1import io.micrometer.observation.ObservationRegistry; 2 3import org.springframework.scheduling.annotation.SchedulingConfigurer; 4import org.springframework.scheduling.config.ScheduledTaskRegistrar; 5 6public class ObservationSchedulingConfigurer implements SchedulingConfigurer { 7 8 private final ObservationRegistry observationRegistry; 9 10 public ObservationSchedulingConfigurer(ObservationRegistry observationRegistry) { 11 this.observationRegistry = observationRegistry; 12 } 13 14 @Override 15 public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 16 taskRegistrar.setObservationRegistry(this.observationRegistry); 17 } 18 19}
이는 기본적으로 org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention을 사용하며, ScheduledTaskObservationContext에 의해 지원됩니다.
Custom 구현은 ObservationRegistry에 직접 구성할 수 있습니다.
Scheduled 메서드 실행 중에 현재 observation은 ThreadLocal 컨텍스트 또는 Reactor 컨텍스트(만약 scheduled 메서드가 Mono 또는 Flux 타입을 반환하는 경우)에 복원됩니다.
기본적으로 다음 KeyValues가 생성됩니다:
| Name | Description |
|---|---|
code.function (required) | 실행이 예약된 Java Method의 이름. |
code.namespace (required) | Scheduled 메서드를 보유하는 빈 인스턴스의 클래스의 canonical name 또는 anonymous 클래스의 경우 "ANONYMOUS". |
error (required) | 실행 중 발생한 예외의 클래스 이름 또는 예외가 발생하지 않은 경우 "none". |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
outcome (required) | 메서드 실행의 결과. "SUCCESS", "ERROR" 또는 "UNKNOWN"(예를 들어 실행 중 작업이 취소된 경우)일 수 있습니다. |
Table 2. Low cardinality Keys
Spring Framework는 classpath에 io.micrometer:micrometer-jakarta9 의존성이 있는 경우 Micrometer에서 제공하는 Jakarta JMS 인스트루멘테이션을 사용합니다.
io.micrometer.jakarta9.instrument.jms.JmsInstrumentation은 jakarta.jms.Session에 인스트루멘테이션을 적용하고 관련 observation을 기록합니다.
이 인스트루멘테이션은 두 가지 타입의 observation을 생성합니다:
JmsTemplate을 사용하여 브로커로 전송될 때 "jms.message.publish".MessageListener 또는 @JmsListener 어노테이션이 있는 메서드로 애플리케이션에 의해 처리될 때 "jms.message.process".| Note |
|---|
현재 "jms.message.receive" observation에 대한 인스트루멘테이션은 없습니다. 메시지 수신을 기다리는 데 소요되는 시간을 측정하는 것은 가치가 크지 않기 때문입니다. 이러한 통합은 일반적으로 MessageConsumer#receive 메서드 호출에 인스트루멘테이션을 적용할 것입니다. 하지만 해당 호출이 반환된 후에는 처리 시간이 측정되지 않으며 trace scope는 애플리케이션으로 전파될 수 없습니다. |
기본적으로 두 observation은 동일한 가능한 KeyValues 집합을 공유합니다:
| Name | Description |
|---|---|
error | 메시징 작업 중 발생한 예외의 클래스 이름(또는 "none"). |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
messaging.destination.temporary (required) | destination이 TemporaryQueue 또는 TemporaryTopic인지 여부 (값: "true" 또는 "false"). |
messaging.operation (required) | 수행 중인 JMS 작업의 이름 (값: "publish" 또는 "process"). |
Table 3. Low cardinality Keys
| Name | Description |
|---|---|
messaging.message.conversation_id | JMS 메시지의 correlation ID. |
messaging.destination.name | 현재 메시지가 전송된 destination의 이름. |
messaging.message.id | 메시지에 대한 identifier로 메시징 시스템에서 사용하는 값. |
Table 4. High cardinality Keys
JMS 메시지가 브로커로 전송될 때 "jms.message.publish" observation이 기록됩니다.
이는 메시지를 보내는 데 소요된 시간을 측정하고 outgoing JMS 메시지 헤더와 함께 tracing 정보를 전파합니다.
Observation을 활성화하려면 JmsTemplate에 ObservationRegistry를 구성해야 합니다:
1import io.micrometer.observation.ObservationRegistry; 2import jakarta.jms.ConnectionFactory; 3 4import org.springframework.jms.core.JmsMessagingTemplate; 5import org.springframework.jms.core.JmsTemplate; 6 7public class JmsTemplatePublish { 8 9 private final JmsTemplate jmsTemplate; 10 11 private final JmsMessagingTemplate jmsMessagingTemplate; 12 13 public JmsTemplatePublish(ObservationRegistry observationRegistry, ConnectionFactory connectionFactory) { 14 this.jmsTemplate = new JmsTemplate(connectionFactory); 15 // configure the observation registry 16 this.jmsTemplate.setObservationRegistry(observationRegistry); 17 18 // For JmsMessagingTemplate, instantiate it with a JMS template that has a configured registry 19 this.jmsMessagingTemplate = new JmsMessagingTemplate(this.jmsTemplate); 20 } 21 22 public void sendMessages() { 23 this.jmsTemplate.convertAndSend("spring.observation.test", "test message"); 24 } 25 26}
이는 기본적으로 io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention을 사용하며, io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext에 의해 지원됩니다.
Listener 메서드에서 response 메시지가 반환될 때 @JmsListener 어노테이션이 있는 메서드에서도 유사한 observation이 기록됩니다.
JMS 메시지가 애플리케이션에 의해 처리될 때 "jms.message.process" observation이 기록됩니다.
이는 메시지를 처리하는 데 소요된 시간을 측정하고 incoming JMS 메시지 헤더와 함께 tracing 컨텍스트를 전파합니다.
대부분의 애플리케이션은 incoming 메시지를 처리하기 위해 @JmsListener 어노테이션이 있는 메서드 메커니즘을 사용할 것입니다.
전용 JmsListenerContainerFactory에 ObservationRegistry가 구성되어 있는지 확인해야 합니다:
1import io.micrometer.observation.ObservationRegistry; 2import jakarta.jms.ConnectionFactory; 3 4import org.springframework.context.annotation.Bean; 5import org.springframework.context.annotation.Configuration; 6import org.springframework.jms.annotation.EnableJms; 7import org.springframework.jms.config.DefaultJmsListenerContainerFactory; 8 9@Configuration 10@EnableJms 11public class JmsConfiguration { 12 13 @Bean 14 public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, ObservationRegistry observationRegistry) { 15 DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); 16 factory.setConnectionFactory(connectionFactory); 17 factory.setObservationRegistry(observationRegistry); 18 return factory; 19 } 20 21}
어노테이션 지원을 활성화하려면 기본 컨테이너 팩토리가 필요하지만,
@JmsListener 어노테이션은 특정 목적을 위해 특정 컨테이너 팩토리 빈을 참조할 수 있다는 점에 유의하십시오.
모든 경우에, observation 레지스트리가 컨테이너 팩토리에 구성된 경우에만 Observation이 기록됩니다.
JmsTemplate에서 메시지가 MessageListener에 의해 처리될 때도 유사한 observation이 기록됩니다.
이러한 listener는 세션 콜백 내의 MessageConsumer에 설정됩니다 (JmsTemplate.execute(SessionCallback<T>) 참조).
이 observation은 기본적으로 io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention을 사용하며, io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext에 의해 지원됩니다.
HTTP 서버 exchange observation은 Servlet 및 Reactive 애플리케이션의 경우 "http.server.requests"라는 이름으로 생성되거나,
OpenTelemetry convention을 사용하는 경우 "http.server.request.duration" 이름으로 생성됩니다.
애플리케이션은 org.springframework.web.filter.ServerHttpObservationFilter Servlet 필터를 애플리케이션에 구성해야 합니다.
이는 웹 프레임워크에서 처리되지 않고 Servlet 필터까지 전파된 Exception인 경우에만 observation을 오류로 기록합니다.
일반적으로 Spring MVC의 @ExceptionHandler 및 ProblemDetail 지원에 의해 처리된 모든 예외는 observation으로 기록되지 않습니다.
Request 처리 중 어느 시점에서든 ObservationContext에서 error 필드를 직접 설정할 수 있습니다:
1import jakarta.servlet.http.HttpServletRequest; 2 3import org.springframework.http.ResponseEntity; 4import org.springframework.stereotype.Controller; 5import org.springframework.web.bind.annotation.ExceptionHandler; 6import org.springframework.web.filter.ServerHttpObservationFilter; 7 8@Controller 9public class UserController { 10 11 @ExceptionHandler(MissingUserException.class) 12 ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) { 13 // We want to record this exception with the observation 14 ServerHttpObservationFilter.findObservationContext(request) 15 .ifPresent(context -> context.setError(exception)); 16 return ResponseEntity.notFound().build(); 17 } 18 19 static class MissingUserException extends RuntimeException { 20 } 21 22}
| Note |
|---|
인스트루멘테이션은 Servlet 필터 수준에서 수행되므로, observation scope는 이 필터 이후에 정렬된 필터와 request 처리만을 포함합니다. 일반적으로 Servlet 컨테이너 오류 처리는 더 낮은 수준에서 수행되며 active observation이나 span이 존재하지 않습니다. 이 사용 사례의 경우 Tomcat용 org.apache.catalina.Valve와 같은 컨테이너별 구현이 필요하며, 이는 이 프로젝트의 범위를 벗어납니다. |
이는 기본적으로 org.springframework.http.server.observation.DefaultServerRequestObservationConvention을 사용하며, ServerRequestObservationContext에 의해 지원됩니다.
기본적으로 다음 KeyValues가 생성됩니다:
| Name | Description |
|---|---|
error (required) | exchange 중 발생한 예외의 클래스 이름 또는 예외가 발생하지 않은 경우 "none". |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
method (required) | HTTP 요청 메서드의 이름 또는 잘 알려진 메서드가 아닌 경우 "none". |
outcome (required) | HTTP 서버 exchange의 결과. |
status (required) | HTTP 응답의 raw 상태 코드 또는 응답이 생성되지 않은 경우 "UNKNOWN". |
uri (required) | 사용 가능한 경우 매칭되는 핸들러의 URI 패턴. 그렇지 않으면 3xx 응답의 경우 REDIRECTION, 404 응답의 경우 NOT_FOUND, path info가 없는 요청의 경우 root, 기타 모든 요청의 경우 UNKNOWN으로 대체됩니다. |
Table 5. Low cardinality Keys
| Name | Description |
|---|---|
http.url (required) | HTTP 요청 URI. |
Table 6. High cardinality Keys
ServerRequestObservationContext에 의해 지원되는 org.springframework.http.server.observation.OpenTelemetryServerRequestObservationConvention을 사용하는 OpenTelemetry variant가 제공됩니다.
이 variant는 HTTP Metrics에 대한 OpenTelemetry Semantic Conventions (v1.36.0)과 HTTP Spans에 대한 OpenTelemetry Semantic Conventions (v1.36.0)을 준수합니다.
애플리케이션은 서버 인스트루멘테이션을 활성화하기 위해 WebHttpHandlerBuilder를 MeterRegistry와 함께 구성해야 합니다.
이는 다음과 같이 WebHttpHandlerBuilder에서 수행할 수 있습니다:
1import io.micrometer.observation.ObservationRegistry; 2 3import org.springframework.context.ApplicationContext; 4import org.springframework.context.annotation.Bean; 5import org.springframework.context.annotation.Configuration; 6import org.springframework.http.server.reactive.HttpHandler; 7import org.springframework.web.server.adapter.WebHttpHandlerBuilder; 8 9@Configuration(proxyBeanMethods = false) 10public class HttpHandlerConfiguration { 11 12 private final ApplicationContext applicationContext; 13 14 public HttpHandlerConfiguration(ApplicationContext applicationContext) { 15 this.applicationContext = applicationContext; 16 } 17 18 @Bean 19 public HttpHandler httpHandler(ObservationRegistry registry) { 20 return WebHttpHandlerBuilder.applicationContext(this.applicationContext) 21 .observationRegistry(registry) 22 .build(); 23 } 24}
이는 기본적으로 org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention을 사용하며, ServerRequestObservationContext에 의해 지원됩니다.
이는 애플리케이션 컨트롤러에서 처리되지 않은 Exception인 경우에만 observation을 오류로 기록합니다.
일반적으로 Spring WebFlux의 @ExceptionHandler 및 ProblemDetail 지원에 의해 처리된 모든 예외는 observation으로 기록되지 않습니다.
Request 처리 중 어느 시점에서든 ObservationContext에서 error 필드를 직접 설정할 수 있습니다:
1import org.springframework.http.ResponseEntity; 2import org.springframework.http.server.reactive.observation.ServerRequestObservationContext; 3import org.springframework.stereotype.Controller; 4import org.springframework.web.bind.annotation.ExceptionHandler; 5import org.springframework.web.server.ServerWebExchange; 6 7@Controller 8public class UserController { 9 10 @ExceptionHandler(MissingUserException.class) 11 ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) { 12 // We want to record this exception with the observation 13 ServerRequestObservationContext.findCurrent(exchange.getAttributes()) 14 .ifPresent(context -> context.setError(exception)); 15 return ResponseEntity.notFound().build(); 16 } 17 18 static class MissingUserException extends RuntimeException { 19 } 20 21}
기본적으로 다음 KeyValues가 생성됩니다:
| Name | Description |
|---|---|
error (required) | exchange 중 발생한 예외의 클래스 이름 또는 예외가 발생하지 않은 경우 "none". |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
method (required) | HTTP 요청 메서드의 이름 또는 잘 알려진 메서드가 아닌 경우 "none". |
outcome (required) | HTTP 서버 exchange의 결과. |
status (required) | HTTP 응답의 raw 상태 코드 또는 응답이 생성되지 않은 경우 "UNKNOWN". |
uri (required) | 사용 가능한 경우 매칭되는 핸들러의 URI 패턴. 그렇지 않으면 3xx 응답의 경우 REDIRECTION, 404 응답의 경우 NOT_FOUND, path info가 없는 요청의 경우 root, 기타 모든 요청의 경우 UNKNOWN으로 대체됩니다. |
Table 7. Low cardinality Keys
| Name | Description |
|---|---|
http.url (required) | HTTP 요청 URI. |
Table 8. High cardinality Keys
HTTP 클라이언트 exchange observation은 blocking 및 reactive 클라이언트의 경우 "http.client.requests"라는 이름으로 생성됩니다.
이 observation은 connection 설정부터 body 디시리얼라이제이션까지 전체 HTTP 요청/응답 exchange를 측정합니다.
서버 counterpart와 달리, 인스트루멘테이션은 클라이언트에 직접 구현되므로 필요한 유일한 단계는 클라이언트에 ObservationRegistry를 구성하는 것입니다.
애플리케이션은 인스트루멘테이션을 활성화하기 위해 RestTemplate 인스턴스에 ObservationRegistry를 구성해야 합니다. 그렇지 않으면 observation은 "no-op"입니다.
Spring Boot는 observation 레지스트리가 이미 설정된 RestTemplateBuilder 빈을 자동 구성합니다.
인스트루멘테이션은 기본적으로 org.springframework.http.client.observation.ClientRequestObservationConvention을 사용하며, ClientRequestObservationContext에 의해 지원됩니다.
| Name | Description |
|---|---|
method (required) | HTTP 요청 메서드의 이름 또는 잘 알려진 메서드가 아닌 경우 "none". |
uri (required) | HTTP 요청에 사용된 URI 템플릿 또는 제공되지 않은 경우 "none". URI의 protocol, host 및 port 부분은 고려되지 않습니다. |
client.name (required) | 요청 URI host에서 유도된 클라이언트 이름. |
status (required) | HTTP 응답의 raw 상태 코드, IOException의 경우 "IO_ERROR", 응답을 수신하지 못한 경우 "CLIENT_ERROR". |
outcome (required) | HTTP 클라이언트 exchange의 결과. |
error (required) | exchange 중 발생한 예외의 클래스 이름 또는 예외가 발생하지 않은 경우 "none". |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
Table 9. Low cardinality Keys
| Name | Description |
|---|---|
http.url (required) | HTTP 요청 URI. |
Table 10. High cardinality Keys
애플리케이션은 인스트루멘테이션을 활성화하기 위해 RestClient.Builder에 ObservationRegistry를 구성해야 합니다. 그렇지 않으면 observation은 "no-op"입니다.
인스트루멘테이션은 기본적으로 org.springframework.http.client.observation.ClientRequestObservationConvention을 사용하며, ClientRequestObservationContext에 의해 지원됩니다.
| Name | Description |
|---|---|
method (required) | HTTP 요청 메서드의 이름 또는 요청을 생성할 수 없었던 경우 "none". |
uri (required) | HTTP 요청에 사용된 URI 템플릿 또는 제공되지 않은 경우 "none". URI의 protocol, host 및 port 부분은 고려되지 않습니다. |
client.name (required) | 요청 URI host에서 유도된 클라이언트 이름. |
status (required) | HTTP 응답의 raw 상태 코드, IOException의 경우 "IO_ERROR", 응답을 수신하지 못한 경우 "CLIENT_ERROR". |
outcome (required) | HTTP 클라이언트 exchange의 결과. |
error (required) | exchange 중 발생한 예외의 클래스 이름 또는 예외가 발생하지 않은 경우 "none". |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
Table 11. Low cardinality Keys
| Name | Description |
|---|---|
http.url (required) | HTTP 요청 URI. |
Table 12. High cardinality Keys
애플리케이션은 인스트루멘테이션을 활성화하기 위해 WebClient.Builder에 ObservationRegistry를 구성해야 합니다. 그렇지 않으면 observation은 "no-op"입니다.
Spring Boot는 observation 레지스트리가 이미 설정된 WebClient.Builder 빈을 자동 구성합니다.
인스트루멘테이션은 기본적으로 org.springframework.web.reactive.function.client.ClientRequestObservationConvention을 사용하며, ClientRequestObservationContext에 의해 지원됩니다.
| Name | Description |
|---|---|
method (required) | HTTP 요청 메서드의 이름 또는 잘 알려진 메서드가 아닌 경우 "none". |
uri (required) | HTTP 요청에 사용된 URI 템플릿 또는 제공되지 않은 경우 "none". URI의 protocol, host 및 port 부분은 고려되지 않습니다. |
client.name (required) | 요청 URI host에서 유도된 클라이언트 이름. |
status (required) | HTTP 응답의 raw 상태 코드, IOException의 경우 "IO_ERROR", 응답을 수신하지 못한 경우 "CLIENT_ERROR". |
outcome (required) | HTTP 클라이언트 exchange의 결과. |
error (required) | exchange 중 발생한 예외의 클래스 이름 또는 예외가 발생하지 않은 경우 "none". |
exception (deprecated) | error 키를 중복하며, 향후 제거될 수 있습니다. |
Table 13. Low cardinality Keys
| Name | Description |
|---|---|
http.url (required) | HTTP 요청 URI. |
Table 14. High cardinality Keys
@EventListenerSpring Framework는 @EventListener 호출에 대해 Observation을 제공하지 않습니다.
이는 그러한 인스트루멘테이션에 적합한 semantics를 가지지 않기 때문입니다.
기본적으로 이벤트 발행과 처리은 동기적으로, 그리고 동일한 스레드에서 수행됩니다. 이는 해당 태스크 실행 중 ThreadLocal과 로깅 컨텍스트가 이벤트 퍼블리셔와 동일하다는 것을 의미합니다.
애플리케이션이 이벤트 처리를 다른 스레드에서 스케줄링하는 전략으로 custom ApplicationEventMulticaster를 global하게 구성하는 경우, 더 이상 그렇지 않습니다.
모든 @EventListener 메서드는 main 이벤트 발행 스레드 외부의 다른 스레드에서 처리됩니다.
이러한 경우, Micrometer Context Propagation 라이브러리는 이러한 값을 전파하고 이벤트 처리의 상관관계를 더 잘 설정하는 데 도움이 될 수 있습니다.
애플리케이션은 선택한 TaskExecutor가 컨텍스트를 전파하는 태스크를 데코레이트하는 ContextPropagatingTaskDecorator를 사용하도록 구성할 수 있습니다.
이를 위해서는 io.micrometer:context-propagation 라이브러리가 classpath에 존재해야 합니다:
1import org.springframework.context.annotation.Bean; 2import org.springframework.context.annotation.Configuration; 3import org.springframework.context.event.SimpleApplicationEventMulticaster; 4import org.springframework.core.task.SimpleAsyncTaskExecutor; 5import org.springframework.core.task.support.ContextPropagatingTaskDecorator; 6 7@Configuration 8public class ApplicationEventsConfiguration { 9 10 @Bean(name = "applicationEventMulticaster") 11 public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() { 12 SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); 13 SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(); 14 // decorate task execution with a decorator that supports context propagation 15 taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator()); 16 eventMulticaster.setTaskExecutor(taskExecutor); 17 return eventMulticaster; 18 } 19 20}
마찬가지로, @EventListener 어노테이션이 있는 각 메서드에 대해 @Async를 추가하는 방식으로 비동기 선택이 local하게 이루어진 경우,
qualifier로 참조하여 컨텍스트를 전파하는 TaskExecutor를 선택할 수 있습니다.
다음과 같이 전용 태스크 데코레이터로 구성된 TaskExecutor 빈 정의가 주어졌다고 가정해 봅시다:
1import org.springframework.context.annotation.Bean; 2import org.springframework.context.annotation.Configuration; 3import org.springframework.core.task.SimpleAsyncTaskExecutor; 4import org.springframework.core.task.TaskExecutor; 5import org.springframework.core.task.support.ContextPropagatingTaskDecorator; 6 7@Configuration 8public class EventAsyncExecutionConfiguration { 9 10 @Bean(name = "propagatingContextExecutor") 11 public TaskExecutor propagatingContextExecutor() { 12 SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(); 13 // decorate task execution with a decorator that supports context propagation 14 taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator()); 15 return taskExecutor; 16 } 17 18}
@Async와 관련 qualifier로 이벤트 리스너에 어노테이션을 추가하면 유사한 컨텍스트 전파 결과를 얻을 수 있습니다:
1import org.apache.commons.logging.Log; 2import org.apache.commons.logging.LogFactory; 3 4import org.springframework.context.event.EventListener; 5import org.springframework.scheduling.annotation.Async; 6import org.springframework.stereotype.Component; 7 8@Component 9public class EmailNotificationListener { 10 11 private final Log logger = LogFactory.getLog(EmailNotificationListener.class); 12 13 @EventListener(EmailReceivedEvent.class) 14 @Async("propagatingContextExecutor") 15 public void emailReceived(EmailReceivedEvent event) { 16 // asynchronously process the received event 17 // this logging statement will contain the expected MDC entries from the propagated context 18 logger.info("email has been received"); 19 } 20 21}
How can I Set the TTL/TTI/Eviction policy/XXX feature?
JVM AOT Cache