可観測性のサポート

Micrometer はアプリケーションでメトリクスとトレースの両方を可能にする観察コンセプト (英語) を定義します。メトリクスのサポートは、アプリケーションの実行時の動作に関する統計を収集するためのタイマー、ゲージ、カウンターを作成する方法を提供します。メトリクスは、エラー率、使用パターン、パフォーマンスなどを追跡できます。トレースは、アプリケーションの境界を越えて、システム全体の全体的なビューを提供します。特定のユーザーリクエストにズームインし、アプリケーション全体でリクエストの補完を追跡することができます。

Spring Framework は、ObservationRegistry が構成されている場合、独自のコードベースのさまざまな部分を計測して観測を公開します。Spring Boot でオブザーバビリティインフラストラクチャの構成について詳しく知ることができます。

生成された観測のリスト

Spring Framework は可観測性のためのさまざまな機能を備えています。このセクションの冒頭で概説したように、設定に応じて、監視によってタイマーメトリクスやトレースを生成できます。

表 1: Spring Framework によって生成された観測
観測名 説明

"http.client.requests"

HTTP クライアント交換に費やされた時間

"http.server.requests"

フレームワークレベルでの HTTP サーバー交換の処理時間

"jms.message.publish"

メッセージプロデューサーが JMS メッセージを宛先に送信するのに費やした時間。

"jms.message.process"

メッセージコンシューマーによって以前に受信された JMS メッセージの処理時間。

"tasks.scheduled.execution"

@Scheduled タスクの実行の処理時間

観測では 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 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();
	}

	@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 が作成されます。

表 2: カーディナリティの低いキー

名前

説明

code.function (必須)

実行がスケジュールされている Java Method の名前。

code.namespace (必須)

スケジュールされたメソッドを保持する Bean インスタンスのクラスの正規名、または匿名クラスの場合は "ANONYMOUS"

error (必須)

実行中にスローされた例外のクラス名、または例外が発生しなかった場合は "none"

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

outcome (必須)

メソッドの実行の結果。"SUCCESS""ERROR"、または "UNKNOWN" のいずれかになります (たとえば、操作が実行中にキャンセルされた場合)。

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 の同じセットを共有します。

表 3: カーディナリティの低いキー

名前

説明

error

メッセージング操作中にスローされた例外のクラス名 (または "none" )。

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

messaging.destination.temporary (必須)

宛先が TemporaryQueue か TemporaryTopic か (値: "true" または "false")。

messaging.operation (必須)

実行されている JMS 操作の名前 (値: "publish" または "process")。

表 4: カーディナリティの高いキー

名前

説明

messaging.message.conversation_id

JMS メッセージの相関 ID。

messaging.destination.name

現在のメッセージの送信先の名前。

messaging.message.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 によってサポートされます。

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 が作成されます。

表 5: カーディナリティの低いキー

名前

説明

error (必須)

交換中にスローされた例外のクラス名、または例外が発生しなかった場合は "none"

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

method (必須)

HTTP リクエストメソッドの名前、または既知のメソッドでない場合は "none"

outcome (必須)

HTTP サーバー交換の結果。

status (必須)

HTTP レスポンスの raw ステータスコード、またはレスポンスが作成されなかった場合は "UNKNOWN"

uri (必須)

一致するハンドラーの URI パターン (使用可能な場合)。3xx レスポンスの場合は REDIRECTION、404 レスポンスの場合は NOT_FOUND、パス情報のないリクエストの場合は root、その他すべてのリクエストの場合は UNKNOWN にフォールバックします。

表 6: カーディナリティの高いキー

名前

説明

http.url (必須)

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 が作成されます。

表 7: カーディナリティの低いキー

名前

説明

error (必須)

交換中にスローされた例外のクラス名、または例外が発生しなかった場合は "none"

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

method (必須)

HTTP リクエストメソッドの名前、または既知のメソッドでない場合は "none"

outcome (必須)

HTTP サーバー交換の結果。

status (必須)

HTTP レスポンスの raw ステータスコード、またはレスポンスが作成されなかった場合は "UNKNOWN"

uri (必須)

一致するハンドラーの URI パターン (使用可能な場合)。3xx レスポンスの場合は REDIRECTION、404 レスポンスの場合は NOT_FOUND、パス情報のないリクエストの場合は root、その他すべてのリクエストの場合は UNKNOWN にフォールバックします。

表 8: カーディナリティの高いキー

名前

説明

http.url (必須)

HTTP リクエスト URI。

HTTP クライアントのインストルメンテーション

HTTP クライアント交換監視は、ブロッキングクライアントとリアクティブクライアント用に "http.client.requests" という名前で作成されます。サーバー版とは異なり、インストルメンテーションはクライアントに直接実装されるため、必要な手順はクライアント上で ObservationRegistry を構成することだけです。

RestTemplate

アプリケーションは、インストルメンテーションを有効にするために、RestTemplate インスタンスで ObservationRegistry を構成する必要があります。それがなければ、観察は「ノーオペレーション」です。Spring Boot は、監視レジストリがすでに設定されている RestTemplateBuilder Bean を自動構成します。

インストルメンテーションはデフォルトで org.springframework.http.client.observation.ClientRequestObservationConvention を使用し、ClientRequestObservationContext によってサポートされます。

表 9: カーディナリティの低いキー

名前

説明

method (必須)

HTTP リクエストメソッドの名前、または既知のメソッドでない場合は "none"

uri (必須)

HTTP リクエストに使用される URI テンプレート、または指定されていない場合は "none"。URI のパス部分のみが考慮されます。

client.name (必須)

リクエスト URI ホストから派生したクライアント名。

status (必須)

HTTP レスポンスの raw ステータスコード、または IOException の場合は "IO_ERROR"、レスポンスが受信されなかった場合は "CLIENT_ERROR"

outcome (必須)

HTTP クライアント交換の結果。

error (必須)

交換中にスローされた例外のクラス名、または例外が発生しなかった場合は "none"

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

表 10: カーディナリティの高いキー

名前

説明

http.url (必須)

HTTP リクエスト URI。

RestClient

アプリケーションは、計測を有効にするには、RestClient.Builder 上で ObservationRegistry を構成する必要があります。それがなければ、観測は「無操作」になります。

インストルメンテーションはデフォルトで org.springframework.http.client.observation.ClientRequestObservationConvention を使用し、ClientRequestObservationContext によってサポートされます。

表 11: カーディナリティの低いキー

名前

説明

method (必須)

HTTP リクエストメソッドの名前、またはリクエストを作成できなかった場合は "none"

uri (必須)

HTTP リクエストに使用される URI テンプレート、または指定されていない場合は "none"。URI のパス部分のみが考慮されます。

client.name (必須)

リクエスト URI ホストから派生したクライアント名。

status (必須)

HTTP レスポンスの raw ステータスコード、または IOException の場合は "IO_ERROR"、レスポンスが受信されなかった場合は "CLIENT_ERROR"

outcome (必須)

HTTP クライアント交換の結果。

error (必須)

交換中にスローされた例外のクラス名、または例外が発生しなかった場合は "none"

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

表 12: カーディナリティの高いキー

名前

説明

http.url (必須)

HTTP リクエスト URI。

WebClient

インストルメンテーションを有効にするには、アプリケーションは WebClient ビルダーで ObservationRegistry を構成する必要があります。それがなければ、観察は「ノーオペレーション」です。Spring Boot は、監視レジストリがすでに設定されている WebClient.Builder Bean を自動構成します。

インストルメンテーションはデフォルトで org.springframework.web.reactive.function.client.ClientRequestObservationConvention を使用し、ClientRequestObservationContext によってサポートされます。

表 13: カーディナリティの低いキー

名前

説明

method (必須)

HTTP リクエストメソッドの名前、または既知のメソッドでない場合は "none"

uri (必須)

HTTP リクエストに使用される URI テンプレート、または指定されていない場合は "none"。URI のパス部分のみが考慮されます。

client.name (必須)

リクエスト URI ホストから派生したクライアント名。

status (必須)

HTTP レスポンスの raw ステータスコード、または IOException の場合は "IO_ERROR"、レスポンスが受信されなかった場合は "CLIENT_ERROR"

outcome (必須)

HTTP クライアント交換の結果。

error (必須)

交換中にスローされた例外のクラス名、または例外が発生しなかった場合は "none"

exception (非推奨)

error キーを複製するため、将来削除される可能性があります。

表 14: カーディナリティの高いキー

名前

説明

http.url (必須)

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");
	}

}