最新の安定バージョンについては、Spring Framework 6.2.8 を使用してください!

可観測性のサポート

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

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

生成された観測のリスト

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

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

"http.client.requests"

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

"http.server.requests"

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

観測では 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 インスタンスを構成できます。

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

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

名前

説明

exception (必須)

交換中にスローされた例外の名前、または例外が発生しなかった場合は KeyValue#NONE_VALUE}。

method (必須)

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

outcome (必須)

HTTP サーバー交換の結果。

status (必須)

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

uri (必須)

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

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

名前

説明

http.url (必須)

HTTP リクエスト URI。

リアクティブアプリケーション

アプリケーションは、アプリケーション内で org.springframework.web.filter.reactive.ServerHttpObservationFilter リアクティブ WebFilter を構成する必要があります。デフォルトでは org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention を使用し、ServerRequestObservationContext によってサポートされます。

これは、Exception が Web フレームワークによって処理されず、WebFilter にバブルアップした場合にのみ、観測をエラーとして記録します。通常、Spring WebFlux の @ExceptionHandler および ProblemDetail のサポートによって処理されるすべての例外は、観測では記録されません。リクエスト処理中のいつでも、ObservationContext のエラーフィールドを自分で設定できます。

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
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
		ServerHttpObservationFilter.findObservationContext(exchange)
				.ifPresent(context -> context.setError(exception));
		return ResponseEntity.notFound().build();
	}

	static class MissingUserException extends RuntimeException {
	}

}

デフォルトでは、次の KeyValues が作成されます。

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

名前

説明

exception (必須)

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

method (必須)

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

outcome (必須)

HTTP サーバー交換の結果。

status (必須)

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

uri (必須)

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

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

名前

説明

http.url (必須)

HTTP リクエスト URI。

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

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

RestTemplate

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

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

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

名前

説明

method (必須)

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

uri (必須)

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

client.name (必須)

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

status (必須)

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

outcome (必須)

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

exception (必須)

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

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

名前

説明

http.url (必須)

HTTP リクエスト URI。

WebClient

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

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

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

名前

説明

method (必須)

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

uri (必須)

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

client.name (必須)

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

status (必須)

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

outcome (必須)

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

exception (必須)

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

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

名前

説明

http.url (必須)

HTTP リクエスト URI。