可観測性のサポート
Micrometer はアプリケーションでメトリクスとトレースの両方を可能にする観察コンセプト (英語) を定義します。メトリクスのサポートは、アプリケーションの実行時の動作に関する統計を収集するためのタイマー、ゲージ、カウンターを作成する方法を提供します。メトリクスは、エラー率、使用パターン、パフォーマンスなどを追跡できます。トレースは、アプリケーションの境界を越えて、システム全体の全体的なビューを提供します。特定のユーザーリクエストにズームインし、アプリケーション全体でリクエストの完了を追跡することができます。
Spring Framework は、ObservationRegistry
が構成されている場合、独自のコードベースのさまざまな部分を計測して観測を公開します。Spring Boot でオブザーバビリティインフラストラクチャの構成について詳しく知ることができます。
生成された観測のリスト
Spring Framework は可観測性のためのさまざまな機能を備えています。このセクションの冒頭で概説したように、設定に応じて、監視によってタイマーメトリクスやトレースを生成できます。
観測名 | 説明 |
---|---|
HTTP クライアント交換に費やされた時間 | |
フレームワークレベルでの HTTP サーバー交換の処理時間 | |
メッセージプロデューサーが JMS メッセージを宛先に送信するのに費やした時間。 | |
メッセージコンシューマーによって以前に受信された JMS メッセージの処理時間。 | |
|
観測では Micrometer の公式命名規則が使用されていますが、メトリクス名は監視システムのバックエンドで推奨される形式 (Prometheus、Atlas、Graphite、InfluxDB など) (英語) に自動的に変換されます。 |
Micrometer 観測概念
Micrometer 観測に詳しくない場合は、知っておくべき概念の簡単な概要を以下に示します。
Observation
は、アプリケーションで起こっていることの実際の記録です。これは、メトリクスまたはトレースを生成するためにObservationHandler
実装によって処理されます。各観測には、対応する
ObservationContext
実装があります。この型には、メタデータを抽出するためのすべての関連情報が保持されます。HTTP サーバー監視の場合、コンテキスト実装は HTTP リクエスト、HTTP レスポンス、処理中にスローされた例外などを保持できます。各
Observation
はKeyValues
メタデータを保持します。HTTP サーバーの監視の場合、これは HTTP リクエストメソッド、HTTP レスポンスステータスなどになります。このメタデータは、サポートするObservationContext
の型を宣言する必要があるObservationConvention
実装によって提供されます。KeyValue
タプルに取り得る値の数が限られている場合、KeyValues
は「カーディナリティが低い」と言われます (HTTP メソッドが良い例です)。低いカーディナリティ値はメトリクスのみにコントリビュートされます。逆に、「高カーディナリティ」値は制限がなく (HTTP リクエスト URI など)、トレースにのみ提供されます。ObservationDocumentation
は、特定のドメイン内のすべての観察結果をドキュメント化し、予想されるキー名とその意味をリストします。
観測の構成
グローバル構成オプションは、ObservationRegistry#observationConfig()
レベルで使用できます。インストルメント化された各コンポーネントは、次の 2 つの拡張ポイントを提供します。
ObservationRegistry
の設定 ; 設定されていない場合、観測は記録されず、ノーオペレーションになります。デフォルトの観測名と抽出された
KeyValues
を変更するためのカスタムObservationConvention
の提供
カスタム観測規則の使用
ServerHttpObservationFilter
を使用した Spring MVC "http.server.requests" メトリクスインスツルメンテーションの例を見てみましょう。この観測では、ServerRequestObservationConvention
と ServerRequestObservationContext
を使用します。カスタム規則はサーブレットフィルターで構成できます。観測によって生成されたメタデータをカスタマイズしたい場合は、要件に合わせて DefaultServerRequestObservationConvention
を継承できます。
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;
public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
// here, we just want to have an additional KeyValue to the observation, keeping the default values
return super.getLowCardinalityKeyValues(context).and(custom(context));
}
private KeyValue custom(ServerRequestObservationContext context) {
return KeyValue.of("custom.method", context.getCarrier().getMethod());
}
}
完全に制御したい場合は、関心のある観察に対する規約契約全体を実装できます。
import java.util.Locale;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.server.observation.ServerHttpObservationDocumentation;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;
public class CustomServerRequestObservationConvention implements ServerRequestObservationConvention {
@Override
public String getName() {
// will be used as the metric name
return "http.server.requests";
}
@Override
public String getContextualName(ServerRequestObservationContext context) {
// will be used for the trace name
return "http " + context.getCarrier().getMethod().toLowerCase(Locale.ROOT);
}
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
return KeyValues.of(method(context), status(context), exception(context));
}
@Override
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
return KeyValues.of(httpUrl(context));
}
private KeyValue method(ServerRequestObservationContext context) {
// You should reuse as much as possible the corresponding ObservationDocumentation for key names
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.METHOD, context.getCarrier().getMethod());
}
// status(), exception(), httpUrl()...
private KeyValue status(ServerRequestObservationContext context) {
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, String.valueOf(context.getResponse().getStatus()));
}
private KeyValue exception(ServerRequestObservationContext context) {
String exception = (context.getError() != null ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE);
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.EXCEPTION, exception);
}
private KeyValue httpUrl(ServerRequestObservationContext context) {
return KeyValue.of(ServerHttpObservationDocumentation.HighCardinalityKeyNames.HTTP_URL, context.getCarrier().getRequestURI());
}
}
カスタム ObservationFilter
を使用して、観測のキー値を追加または削除するなど、同様のゴールを達成することもできます。フィルターはデフォルトの規則を置き換えるものではなく、後処理コンポーネントとして使用されます。
import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationFilter;
import org.springframework.http.server.observation.ServerRequestObservationContext;
public class ServerRequestObservationFilter implements ObservationFilter {
@Override
public Observation.Context map(Observation.Context context) {
if (context instanceof ServerRequestObservationContext serverContext) {
context.setName("custom.observation.name");
context.addLowCardinalityKeyValue(KeyValue.of("project", "spring"));
String customAttribute = (String) serverContext.getCarrier().getAttribute("customAttribute");
context.addLowCardinalityKeyValue(KeyValue.of("custom.attribute", customAttribute));
}
return context;
}
}
ObservationRegistry
で ObservationFilter
インスタンスを構成できます。
@Scheduled タスクのインストルメンテーション
観測は、@Scheduled
タスクの実行ごとに作成されます。アプリケーションは、観測の記録を有効にするには、ScheduledTaskRegistrar
上で ObservationRegistry
を構成する必要があります。これは、監視レジストリを設定する SchedulingConfigurer
Bean を宣言することで実行できます。
import io.micrometer.observation.ObservationRegistry;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
public class ObservationSchedulingConfigurer implements SchedulingConfigurer {
private final ObservationRegistry observationRegistry;
public ObservationSchedulingConfigurer(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setObservationRegistry(this.observationRegistry);
}
}
デフォルトでは org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention
を使用し、ScheduledTaskObservationContext
によってサポートされます。ObservationRegistry
でカスタム実装を直接構成できます。スケジュールされたメソッドの実行中、現在の監視は ThreadLocal
コンテキストまたは Reactor コンテキスト (スケジュールされたメソッドが Mono
または Flux
型を返した場合) に復元されます。
デフォルトでは、次の KeyValues
が作成されます。
名前 | 説明 |
| 実行がスケジュールされている Java |
| スケジュールされたメソッドを保持する Bean インスタンスのクラスの正規名、または匿名クラスの場合は |
| 実行中にスローされた例外のクラス名、または例外が発生しなかった場合は |
|
|
| メソッドの実行の結果。 |
JMS メッセージングインストルメンテーション
io.micrometer:micrometer-jakarta9
依存関係がクラスパス上にある場合、Spring Framework は Micrometer によって提供される Jakarta JMS インストルメンテーションを使用します。io.micrometer.jakarta9.instrument.jms.JmsInstrumentation
は jakarta.jms.Session
を計測し、関連する観測値を記録します。
この計測器は 2 種類の観測を作成します。
"jms.message.publish"
JMS メッセージがブローカーに送信される場合、通常はJmsTemplate
を使用します。"jms.message.process"
: JMS メッセージがアプリケーションによって (通常はMessageListener
または@JmsListener
アノテーション付きメソッドを使用して) 処理される場合。
メッセージの受信を待機するのに費やされる時間を測定することにほとんど価値がないため、現在、"jms.message.receive" 観測用の計測器はありません。このような統合では、通常、MessageConsumer#receive メソッド呼び出しが計測されます。ただし、それらが返されると、処理時間は測定されず、トレーススコープをアプリケーションに伝播することはできません。 |
デフォルトでは、両方の観測は可能な KeyValues
の同じセットを共有します。
名前 | 説明 |
| メッセージング操作中にスローされた例外のクラス名 (または "none" )。 |
|
|
| 宛先が |
| 実行されている JMS 操作の名前 (値: |
名前 | 説明 |
| JMS メッセージの相関 ID。 |
| 現在のメッセージの送信先の名前。 |
| メッセージングシステムによってメッセージの識別子として使用される値。 |
JMS メッセージパブリケーションのインストルメンテーション
"jms.message.publish"
観察は、JMS メッセージがブローカーに送信されるときに記録されます。これらは、メッセージの送信に費やされた時間を測定し、送信 JMS メッセージヘッダーを使用してトレース情報を伝播します。
監視を有効にするには、JmsTemplate
上で ObservationRegistry
を構成する必要があります。
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
public class JmsTemplatePublish {
private final JmsTemplate jmsTemplate;
private final JmsMessagingTemplate jmsMessagingTemplate;
public JmsTemplatePublish(ObservationRegistry observationRegistry, ConnectionFactory connectionFactory) {
this.jmsTemplate = new JmsTemplate(connectionFactory);
// configure the observation registry
this.jmsTemplate.setObservationRegistry(observationRegistry);
// For JmsMessagingTemplate, instantiate it with a JMS template that has a configured registry
this.jmsMessagingTemplate = new JmsMessagingTemplate(this.jmsTemplate);
}
public void sendMessages() {
this.jmsTemplate.convertAndSend("spring.observation.test", "test message");
}
}
デフォルトでは io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention
を使用し、io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext
によってサポートされます。
リスナーメソッドからレスポンスメッセージが返されるときに、@JmsListener
アノテーションが付けられたメソッドでも同様の観察結果が記録されます。
JMS メッセージ処理インスツルメンテーション
"jms.message.process"
観察は、JMS メッセージがアプリケーションによって処理されるときに記録されます。これらは、メッセージの処理に費やされた時間を測定し、受信 JMS メッセージヘッダーを使用してトレースコンテキストを伝播します。
ほとんどのアプリケーションは、@JmsListener
アノテーション付きメソッドメカニズムを使用して受信メッセージを処理します。ObservationRegistry
が専用 JmsListenerContainerFactory
上で構成されていることを確認する必要があります。
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
@Configuration
@EnableJms
public class JmsConfiguration {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, ObservationRegistry observationRegistry) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setObservationRegistry(observationRegistry);
return factory;
}
}
アノテーションのサポートを有効にするにはデフォルトのコンテナーファクトリが必要ですが、@JmsListener
アノテーションは特定の目的で特定のコンテナーファクトリ Bean を参照できることに注意してください。いずれの場合も、観察レジストリがコンテナーファクトリで構成されている場合にのみ、観察が記録されます。
メッセージが MessageListener
によって処理される場合、同様の観察が JmsTemplate
で記録されます。このようなリスナーは、セッションコールバック内の MessageConsumer
に設定されます ( JmsTemplate.execute(SessionCallback<T>)
を参照)。
この観測では、デフォルトで io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention
が使用され、io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext
によってサポートされます。
HTTP サーバーの計測
HTTP サーバー交換監視は、サーブレットおよびリアクティブアプリケーション用に "http.server.requests"
という名前で作成されます。
サーブレットアプリケーション
アプリケーションは、アプリケーション内で org.springframework.web.filter.ServerHttpObservationFilter
サーブレットフィルターを構成する必要があります。デフォルトでは org.springframework.http.server.observation.DefaultServerRequestObservationConvention
を使用し、ServerRequestObservationContext
によってサポートされます。
これは、Exception
が Web フレームワークによって処理されず、サーブレットフィルターにバブルアップした場合にのみ、観察をエラーとして記録します。通常、Spring MVC の @ExceptionHandler
および ProblemDetail
サポートによって処理されるすべての例外は、観測結果とともに記録されません。リクエスト処理中のいつでも、ObservationContext
のエラーフィールドを自分で設定できます。
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.ServerHttpObservationFilter;
@Controller
public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) {
// We want to record this exception with the observation
ServerHttpObservationFilter.findObservationContext(request)
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
static class MissingUserException extends RuntimeException {
}
}
インストルメンテーションはサーブレットフィルターレベルで行われるため、監視スコープは、このフィルターの後に順序付けされたフィルターとリクエストの処理のみをカバーします。通常、サーブレットコンテナーのエラー処理は下位レベルで実行され、アクティブな監視やスパンはありません。この使用例では、Tomcat の org.apache.catalina.Valve などのコンテナー固有の実装が必要です。これはこのプロジェクトの範囲外です。 |
デフォルトでは、次の KeyValues
が作成されます。
名前 | 説明 |
| 交換中にスローされた例外のクラス名、または例外が発生しなかった場合は |
|
|
| HTTP リクエストメソッドの名前、または既知のメソッドでない場合は |
| HTTP サーバー交換の結果。 |
| HTTP レスポンスの raw ステータスコード、またはレスポンスが作成されなかった場合は |
| 一致するハンドラーの URI パターン (使用可能な場合)。3xx レスポンスの場合は |
名前 | 説明 |
| HTTP リクエスト URI。 |
リアクティブアプリケーション
サーバー計測を有効にするには、アプリケーションで WebHttpHandlerBuilder
を MeterRegistry
で構成する必要があります。これは、次のように WebHttpHandlerBuilder
で実行できます。
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
@Configuration(proxyBeanMethods = false)
public class HttpHandlerConfiguration {
private final ApplicationContext applicationContext;
public HttpHandlerConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public HttpHandler httpHandler(ObservationRegistry registry) {
return WebHttpHandlerBuilder.applicationContext(this.applicationContext)
.observationRegistry(registry)
.build();
}
}
デフォルトでは org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention
を使用し、ServerRequestObservationContext
によってサポートされます。
これは、Exception
がアプリケーションコントローラーによって処理されなかった場合にのみ、観察をエラーとして記録します。通常、Spring WebFlux の @ExceptionHandler
および ProblemDetail
サポートによって処理されるすべての例外は、観測では記録されません。リクエスト処理中のいつでも、ObservationContext
のエラーフィールドを自分で設定できます。
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ServerWebExchange;
@Controller
public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
// We want to record this exception with the observation
ServerRequestObservationContext.findCurrent(exchange.getAttributes())
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
static class MissingUserException extends RuntimeException {
}
}
デフォルトでは、次の KeyValues
が作成されます。
名前 | 説明 |
| 交換中にスローされた例外のクラス名、または例外が発生しなかった場合は |
|
|
| HTTP リクエストメソッドの名前、または既知のメソッドでない場合は |
| HTTP サーバー交換の結果。 |
| HTTP レスポンスの raw ステータスコード、またはレスポンスが作成されなかった場合は |
| 一致するハンドラーの URI パターン (使用可能な場合)。3xx レスポンスの場合は |
名前 | 説明 |
| HTTP リクエスト URI。 |
HTTP クライアントのインストルメンテーション
HTTP クライアント交換監視は、ブロッキングクライアントとリアクティブクライアント用に "http.client.requests"
という名前で作成されます。サーバー版とは異なり、インストルメンテーションはクライアントに直接実装されるため、必要な手順はクライアント上で ObservationRegistry
を構成することだけです。
RestTemplate
アプリケーションは、インストルメンテーションを有効にするために、RestTemplate
インスタンスで ObservationRegistry
を構成する必要があります。それがなければ、観察は「ノーオペレーション」です。Spring Boot は、監視レジストリがすでに設定されている RestTemplateBuilder
Bean を自動構成します。
インストルメンテーションはデフォルトで org.springframework.http.client.observation.ClientRequestObservationConvention
を使用し、ClientRequestObservationContext
によってサポートされます。
名前 | 説明 |
| HTTP リクエストメソッドの名前、または既知のメソッドでない場合は |
| HTTP リクエストに使用される URI テンプレート、または指定されていない場合は |
| リクエスト URI ホストから派生したクライアント名。 |
| HTTP レスポンスの raw ステータスコード、または |
| HTTP クライアント交換の結果。 |
| 交換中にスローされた例外のクラス名、または例外が発生しなかった場合は |
|
|
名前 | 説明 |
| HTTP リクエスト URI。 |
RestClient
アプリケーションは、計測を有効にするには、RestClient.Builder
上で ObservationRegistry
を構成する必要があります。それがなければ、観測は「無操作」になります。
インストルメンテーションはデフォルトで org.springframework.http.client.observation.ClientRequestObservationConvention
を使用し、ClientRequestObservationContext
によってサポートされます。
名前 | 説明 |
| HTTP リクエストメソッドの名前、またはリクエストを作成できなかった場合は |
| HTTP リクエストに使用される URI テンプレート、または指定されていない場合は |
| リクエスト URI ホストから派生したクライアント名。 |
| HTTP レスポンスの raw ステータスコード、または |
| HTTP クライアント交換の結果。 |
| 交換中にスローされた例外のクラス名、または例外が発生しなかった場合は |
|
|
名前 | 説明 |
| HTTP リクエスト URI。 |
WebClient
インストルメンテーションを有効にするには、アプリケーションは WebClient
ビルダーで ObservationRegistry
を構成する必要があります。それがなければ、観察は「ノーオペレーション」です。Spring Boot は、監視レジストリがすでに設定されている WebClient.Builder
Bean を自動構成します。
インストルメンテーションはデフォルトで org.springframework.web.reactive.function.client.ClientRequestObservationConvention
を使用し、ClientRequestObservationContext
によってサポートされます。
名前 | 説明 |
| HTTP リクエストメソッドの名前、または既知のメソッドでない場合は |
| HTTP リクエストに使用される URI テンプレート、または指定されていない場合は |
| リクエスト URI ホストから派生したクライアント名。 |
| HTTP レスポンスの raw ステータスコード、または |
| HTTP クライアント交換の結果。 |
| 交換中にスローされた例外のクラス名、または例外が発生しなかった場合は |
|
|
名前 | 説明 |
| HTTP リクエスト URI。 |
アプリケーションイベントと @EventListener
Spring Framework は、そのような計測に適切なセマンティクスを持たないため、@EventListener
コールの観測には貢献しません。デフォルトでは、イベントの発行と処理は同じスレッド上で同期的に行われます。これは、そのタスクの実行中、ThreadLocals およびログコンテキストがイベントパブリッシャーと同じになることを意味します。
アプリケーションが、さまざまなスレッドでイベント処理をスケジュールする戦略を使用してカスタム ApplicationEventMulticaster
をグローバルに構成する場合、これは当てはまりません。すべての @EventListener
メソッドは、メインイベントパブリケーションスレッドの外側の別のスレッドで処理されます。このような場合、Micrometer コンテキスト伝播ライブラリ (英語) はそのような値を伝播し、イベントの処理をより適切に関連付けることができます。アプリケーションは、タスクを修飾してコンテキストを伝播する ContextPropagatingTaskDecorator
を使用するように、選択した TaskExecutor
を構成できます。これが機能するには、io.micrometer:context-propagation
ライブラリがクラスパス上に存在する必要があります。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
@Configuration
public class ApplicationEventsConfiguration {
@Bean(name = "applicationEventMulticaster")
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}
}
同様に、その非同期選択が @EventListener
アノテーション付きメソッドごとにローカルで行われる場合、それに @Async
を追加することで、修飾子で参照することでコンテキストを伝播する TaskExecutor
を選択できます。次の TaskExecutor
Bean 定義があり、専用のタスクデコレータで構成されているとします。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
@Configuration
public class EventAsyncExecutionConfiguration {
@Bean(name = "propagatingContextExecutor")
public TaskExecutor propagatingContextExecutor() {
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
return taskExecutor;
}
}
イベントリスナーに @Async
と関連する修飾子でアノテーションを付けると、同様のコンテキスト伝播結果が得られます。
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class EmailNotificationListener {
private final Log logger = LogFactory.getLog(EmailNotificationListener.class);
@EventListener(EmailReceivedEvent.class)
@Async("propagatingContextExecutor")
public void emailReceived(EmailReceivedEvent event) {
// asynchronously process the received event
// this logging statement will contain the expected MDC entries from the propagated context
logger.info("email has been received");
}
}