![[17.0.0.3 and later]](../ng_v17003plus.gif)
MicroProfile メトリック計測 API
MicroProfile Metrics API を使用して、アプリケーションにメトリックを追加できます。MicroProfile Metrics API は、Dropwizard Metrics API に似ています。
MicroProfile Metrics のアーキテクチャー
- ベース
- ベース・レジストリーは、MicroProfile Metrics 仕様からの必要に応じて、サーバーからのメトリック用に使用されます。
- ベンダー
- ベンダー・レジストリーは、製品ベンダー (このケースでは Liberty) によって追加されるメトリック用に使用されます。
- アプリケーション
- アプリケーション・レジストリーは、アプリケーションによって追加されるメトリック用に使用されます。アプリケーション・レジストリーは、デフォルトの MetricRegistry です。
mpMetrics-1.0 フィーチャーおよび mpMetrics-1.1 フィーチャーには、ブラウザーおよびモニタリング・ツールと対話するための HTTP インターフェースを提供する /metrics エンドポイントも含まれています。
メトリックの値の確認
MicroProfile Metrics では、HTTP を使用してアクセスできる /metrics エンドポイントが提供されています。Liberty サーバーが適切な構成で開始されると、それ以降は任意のブラウザーでメトリックを表示できます。
https://localhost:9443/metrics
一般的に、長期間にわたってメトリック値をモニターおよび記録するためのツールを運用チームが構成します。
アプリケーションへのメトリックの追加
- MetricRegistry を直接使用します。この方法では、アプリケーション・コードが明示的にメトリックを作成し、登録します。
- CDI を使用してメトリックを注入します。この方法では、メトリックは CDI によって暗黙的に作成され、アプリケーション MetricRegistry に登録されます。
- メトリック・アノテーションを使用します。この方法では、メトリックは CDI によって暗黙的に作成され、アプリケーション MetricRegistry に登録されます。
MetricRegistry の直接管理
MetricRegistry は、すべてのメトリックと、各メトリックのメタデータを保管します。提供されているメソッドを使用して、メトリックおよびメタデータを登録および取得できます。デフォルトでは、アプリケーション関連のすべてのメトリックが、アプリケーションをスコープとする単一の MetricRegistry に追加されます。
@Inject
MetricRegistry registry;
メトリックの登録
MetricRegistry クラスには、カウンター、メーター、ヒストグラム、およびタイマーの取得または作成を行うことのできるメソッドがあります。
Metadata counterMetadata = new Metadata(…);
…
// Create or retrieve the metric by Metadata.getName()
Counter counterMetric = registry.counter(counterMetadata);
Timer timerMetric = registry.timer(timerMetadata);
Histogram histogramMetric = registry.histogram(histogramMetadata);
Meter meterMetric = registry.meter(meterMetadata);
// Register a gauge metric by Metadata.getName()
registry.register("gaugeMetric", gaugeMetric, gaugeMetadata);
メタデータによるメトリックの記述
メタデータは、メトリックについての情報を記述および要約するデータの集合であり、さまざまなタイプのメトリックの検出および処理を簡単に行えるようにできます。メタデータは属性レベルで情報を定義するのに十分な細分度にできます。
メトリックのメタデータは、以下の属性からなります。
- 単位
- ストリング単位の固定セット。
- タイプ
- ゲージ、カウンター、メーター、ヒストグラム、またはタイマー。
- 説明
- 人間が読めるメトリック説明。
- 表示名
- メトリック名が人間が読めるものでない場合 (例えば、メトリック名が UUID である場合)、表示目的のための人間が読めるメトリック名。
- タグ
- キーと値 (key=value) のペアのコンマ区切りリスト。
再使用可能
MicroProfile Metrics バージョン 1.1 以上の場合、再使用可能は、メトリック名が再使用可能かどうかを示すブール値です。例えば、複数のメソッドにアノテーションを付けるときに、単一のメトリックを再使用したい場合などです。
export MP_METRICS_TAGS=app=shop,node=2932
MicroProfile 構成を使用して MP_METRICS_TAGS を構成することもできます。 MP_METRICS_TAGS を構成する方法について詳しくは、Liberty ランタイムにおける有効な構成値のロケーションを参照してください。
カウンター
カウンターは、増分または減分のカウントを保持するために使用されるメトリックです。カウンターの初期値は 0 に設定され、 inc() または inc(long n) を使用して増分したり、dec() または dec(long n) を使用して減分したりできます。
カウンターを使用して、受け取った要求の総数を数えたり、同時にアクティブな HTTP セッションの総数を数えたりできます。
Metadata statsHitsCounterMetadata = new Metadata(
"statsHits", // name
"Stats Hits", // display name
"Number of hits on the /stats endpoint", // description
MetricType.COUNTER, // type
MetricUnits.NONE); // units
Counter statsHitsCounter = registry.counter(statsHitsCounterMetadata);
@GET
@Path("/donations")
public String getTotalDonations() {
statsHitsCounter.inc();
return "$" + donationManager.getTotalDonations();
}
curl -k -u user:password https://localhost:9443/metrics/application/statsHits
# TYPE application:stats_hits counter
# HELP application:stats_hits Number of hits on the /stats endpoint
application:stats_hits 213
curl -k -u user:password -H “Accept: application/json” https://localhost:9443/metrics/application/statsHits
{"statsHits":213}
ゲージ
ゲージは、値を取得するためにサンプリングされるメトリックを表します。
ゲージは、開発者が実装する必要のあるインターフェースです。ゲージの実装は定義されていないため、 MetricRegistry .register() メソッドを使用して MetricRegistry に手動で登録される必要があります。必ず、メトリック当たり 1 回のみ register() が使用されるようにしてください。同じ名前で 2 つのメトリックを登録することはできません。ゲージは、CPU 温度やディスク使用量を計測するために使用されます。
Gauge<Double> progressGauge = new Gauge<Double>() {
public Double getValue() {
return (double) getTotalDonations()
/ (double) getGoal() * 100.0;
}
};
// Gauge
Metadata progressMetadata = new Metadata(
"progress", // name
"Donation Progress", // display name
"The percentage of the goal achieved so far", // description
MetricType.GAUGE, // type
MetricUnits.PERCENT); // units
registry.register(progressMetadata.getName(), progressGauge, progressMetadata);
curl -k -u user:password https://localhost:9443/metrics/application/progress
# TYPE application:progress_percent gauge
# HELP application:progress_percent The percentage of the goal achieved so far
application:progress_percent 4.472
curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/progress
{"progress":4.472}
メーター
メーターは、スループットを追跡するために使用されます。
- 平均スループット。
- 1 分 / 5 分 / 15 分の指数加重移動平均スループット。
- 測定の数。
メーターを使用して、アプリケーションによるトランザクションの処理レートを計算できます。
Metadata getProgressMeterMetadata = new Metadata(
"getProgressMeter", // name
"getProgress Call Rate", // display name
"The rate of getProgress calls", // description
MetricType.METERED, // type
MetricUnits.SECONDS); // units
Meter getProgressMeter = registry.meter(getProgressMeterMetadata);
public Double getProgress() {
getProgressMeter.mark();
return (double) getTotalDonations()/(double) getGoal() * 100.0;
}
curl -k -u user:password https://localhost:9443/metrics/application/getProgressMeter1
# TYPE application:get_progress_meter_total counter
# HELP application:get_progress_meter_total The rate of getProgress calls
application:get_progress_meter_total 78
# TYPE application:get_progress_meter_rate_per_second gauge
application:get_progress_meter_rate_per_second 0.6584919803150174
# TYPE application:get_progress_meter_one_min_rate_per_second gauge
application:get_progress_meter_one_min_rate_per_second 0.5851884005757912
# TYPE application:get_progress_meter_five_min_rate_per_second gauge
application:get_progress_meter_five_min_rate_per_second 1.4218610926179416
# TYPE application:get_progress_meter_fifteen_min_rate_per_second gauge
application:get_progress_meter_fifteen_min_rate_per_second 1.6627979138032118
curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/getProgressMeter
{
"getProgressMeter": {
"count": 78,
"fifteenMinRate": 1.6627979138032118,
"fiveMinRate": 1.4218610926179416,
"meanRate": 0.6584919803150174,
"oneMinRate": 0.5851884005757912
}
}
ヒストグラム
ヒストグラムは、値の分布を保管するために使用されます。
ヒストグラムで値を記録するには、記録したい値を指定して histogram.update(long value) を呼び出す必要があります。getSnapshot() を使用することによって、ヒストグラムの現在の状態 (またはスナップショット) を取得できます。MicroProfile Metrics では、ヒストグラムは整数または long 値 のみをサポートします。
- 最大値 / 最小値 / 平均値
- 50 番目、75 番目、95 番目、98 番目、99 番目、99.9 番目のパーセンタイルの値
- 値の数
ヒストグラムの例には、取得されるペイロード・サイズの分布や、世帯収入の分布を収集する新規雇用調査などがあります。
Metadata donationDistributionMetadata = new Metadata(
"donationDistribution", // name
"Donation Distribution", // display name
"The distribution of the donation amounts ", // description
MetricType.HISTOGRAM, // type
“dollars”); // units
Histogram donationDistribution = registry.histogram(donationDistributionMetadata);
public void addDonation(Long amount) {
totalDonations += amount;
donations.add(amount);
donationDistribution.update(amount);
}
curl -k -u user:password https://localhost:9443/metrics/application/com.example.samples.donationapp.DonationManager.donationDistribution
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_mean_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_mean_dollars 19.300015535407777
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_max_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_max_dollars 102.0
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_min_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_min_dollars 3.0
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_stddev_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_stddev_dollars 26.35464238355834
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars summary
# HELP application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars The distribution of the donation amounts
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars_count 203
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.5"} 5.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.75"} 24.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.95"} 83.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.98"} 93.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.99"} 101.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.999"} 102.0
curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/com.example.samples.donationapp.DonationManager.donationDistribution
{
"com.example.samples.donationapp.DonationManager.donationDistribution": {
"count": 203,
"max": 102,
"mean": 19.300015535407777,
"min": 3,
"p50": 5.0,
"p75": 24.0,
"p95": 83.0,
"p98": 93.0,
"p99": 101.0,
"p999": 102.0,
"stddev": 26.35464238355834
}
}
タイマー
タイマーは、時間 (ナノ秒単位) を集約するために使用され、期間およびスループットの統計を提供します。
コードの一部の時間を測るため、timer.time() を呼び出すことができます。 これは、timer.context オブジェクトを返します。このコンテキストは、context.close() を呼び出してタイマーを停止するために使用されます。タイマーから取得される情報は、測定される時間についてメーターとヒストグラムを組み合わせたものです。
- 最大時間 / 最小時間 / 平均時間。
- 50 番目、75 番目、95 番目、98 番目、99 番目、99.9 番目のパーセンタイルの時間値。
- 平均スループット。
- 1 分 / 5 分 / 15 分の指数加重移動平均スループット。
- 時間測定されるイベントの数。
タイマーの使用目的の例には、応答時間の測定、複雑なロジックの処理時間の測定などがあります。
Metadata topDonationCalcTimerMetadata = new Metadata(
"topDonationCalcTimer", // name
"Top Donation Calculation Time", // display name
"Processing time to find the top donation", // description
MetricType.TIMER, // type
MetricUnits.NANOSECONDS); // units
Timer topDonationCalcTimer = registry.timer(topDonationCalcTimerMetadata);
public Long getTopDonation() {
// Start timing here
Timer.Context context = topDonationCalcTimer.time();
Long max = 0L;
for (Long amount : donationManager.getDonationsList()) {
max = amount > max ? amount : max;
}
// Stop timing
context.close();
return max;
}
curl -k -u user:password https://localhost:9443/metrics/application/topDonationCalcTimer
# TYPE application:top_donation_calc_timer_rate_per_second gauge
application:top_donation_calc_timer_rate_per_second 0.19107302754898328
# TYPE application:top_donation_calc_timer_one_min_rate_per_second gauge
application:top_donation_calc_timer_one_min_rate_per_second 0.013233974568205872
# TYPE application:top_donation_calc_timer_five_min_rate_per_second gauge
application:top_donation_calc_timer_five_min_rate_per_second 0.4845914744130395
# TYPE application:top_donation_calc_timer_fifteen_min_rate_per_second gauge
application:top_donation_calc_timer_fifteen_min_rate_per_second 0.8866728789348088
# TYPE application:top_donation_calc_timer_mean_seconds gauge
application:top_donation_calc_timer_mean_seconds 9.37780684853573E-5
# TYPE application:top_donation_calc_timer_max_seconds gauge
application:top_donation_calc_timer_max_seconds 1.97197E-4
# TYPE application:top_donation_calc_timer_min_seconds gauge
application:top_donation_calc_timer_min_seconds 4.9630000000000004E-5
# TYPE application:top_donation_calc_timer_stddev_seconds gauge
application:top_donation_calc_timer_stddev_seconds 3.082659934664267E-5
# TYPE application:top_donation_calc_timer_seconds summary
# HELP application:top_donation_calc_timer_seconds Processing time to find the top donation
application:top_donation_calc_timer_seconds_count 63
application:top_donation_calc_timer_seconds{quantile="0.5"} 8.6069E-5
application:top_donation_calc_timer_seconds{quantile="0.75"} 1.0372E-4
application:top_donation_calc_timer_seconds{quantile="0.95"} 1.53694E-4
application:top_donation_calc_timer_seconds{quantile="0.98"} 1.96615E-4
application:top_donation_calc_timer_seconds{quantile="0.99"} 1.97197E-4
application:top_donation_calc_timer_seconds{quantile="0.999"} 1.97197E-4
curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/topDonationCalcTimer
{
"topDonationCalcTimer": {
"count": 63,
"fifteenMinRate": 0.8866728789348088,
"fiveMinRate": 0.4845914744130395,
"max": 197197,
"mean": 93778.06848535729,
"meanRate": 0.19107302754898328,
"min": 49630,
"oneMinRate": 0.013233974568205872,
"p50": 86069.0,
"p75": 103720.0,
"p95": 153694.0,
"p98": 196615.0,
"p99": 197197.0,
"p999": 197197.0,
"stddev": 30826.599346642666
}
}
CDI を使用したメトリックの注入
- @Metric: 注入されるメトリックを記述するアノテーション。このアノテーションは、タイプがメーター、タイマー、カウンター、およびヒストグラムのフィールドで使用できます。
- @Metric パラメーター: name、displayname、units、tags、description、absolute。
@Metric アノテーションのパラメーターは、対応するメタデータ・フィールドに似ていますが、以下の相違点があります。サーバーは、カウンターを作成して MetricRegistry に登録し、アプリケーションで使用できるようにそのカウンターを提供します。カウンター例は、statsHitsCounter 例と等価です。absolute=true は、提供された名前がそのまま使用されることを意味します。units パラメーターは使用されていないため、デフォルトで MetricUnits.NONE に設定されます。
- tags: key=value タグ・ストリングの配列
- absolute: true の場合、メトリック名を name パラメーターに指定されたとおりの名前に設定します。false の場合、パッケージ・クラスおよび名前に接頭部を追加して、完全修飾名を使用します。
@Inject @Metric(name="statsHits", displayName="Stats Hits", description="Number of hits on the /stats endpoint", absolute=true) Counter statsHitsCounter;
@Inject @Metric(name= "topDonationCalcTimer", unit = MetricUnits.NANOSECONDS, description="Processing time to find the top donation") Timer topDonationCalcTimer; @Inject @Metric(absolute = true, tags=["appName=DonationApp"]) Counter counted;
メトリックのアノテーション
MicroProfile Metrics には、@Counted アノテーション、@Timed アノテーション、@Metered アノテーション、および @Gauge アノテーションを処理するための多くのインターセプターもあります。
- @Counted
- メソッド、コンストラクター、またはタイプをカウンターとしてマーク付けるアノテーション。メタデータのフィールドと共に、カウンターには追加で monotonic フィールドもあります。monotonic が false に設定されている場合、アノテーションを付けられたメソッドが呼び出されるとカウンターは増分され、戻ると減分されて、アノテーションを付けられたメソッドの現在の呼び出し数がカウントされます。monotonic が true に設定されている場合、カウンターは単調に増分されて、アノテーションを付けられたメソッドの呼び出し総数がカウントされます。注: デフォルトでは、monotonic は false に設定されます。
@GET @Path("/no") @Counted(name="no", displayName="No donation count", description="Number of people that declined to donate.", monotonic=true) public String noDonation() { return "Maybe next time!"; }
- @Timed
- アノテーション付きオブジェクトのコンストラクターまたはメソッドをタイマーとしてマーク付けるアノテーション。タイマー・メトリックは、アノテーション付きオブジェクトが開始される頻度を追跡し、呼び出しが完了するまでにかかる時間を追跡します。
@POST @Path("/creditcard") @Timed( name="donateAmountViaCreditCard.timer", displayName="Donations Via Credit Cards", description = "Donations that were made using a credit card") public String donateAmountViaCreditCard(@FormParam("amount") Long amount, @FormParam("card") String card) { if (processCard(card, amount)) return "Thanks for donating!"; return "Sorry, please try again."; }
- @Metered
- コンストラクターまたはメソッドをメーターとしてマーク付けるアノテーション。メーターは、コンストラクターまたはメソッドの呼び出しをカウントし、呼び出される頻度を追跡します。
@Metered(displayName="Rate of donations", description="Rate of incoming donations (the instances not the amount)") public void addDonation(Long amount) { totalDonations += amount; donations.add(amount); donationDistribution.update(amount); }
- @Gauge
- メソッドをゲージとしてマーク付けるアノテーション。
@Gauge( name="donations", displayName="Total Donations", description="Total amount of money raised for charity!", unit = "dollars", absolute=true) public Long getTotalDonations(){ return totalDonations; }
![[18.0.0.1 and later]](../ng_v18001plus.gif)
- メトリックの再使用
- MicroProfile Metrics バージョン 1.1 以上を使用している場合、メトリックを再使用できます。@Counted アノテーション、@Metered アノテーション、および @Timed アノテーションには、メトリックが他のアノテーションで再使用可能であることを示すフラグがあります。例として、異なる支払い方法に応じて複数のエンドポイントがあるアプリケーションを想定してみましょう。reusable=true を設定することによって、支払い回数を追跡するために、すべての支払い方法で同じメトリック名を使用できます。以下に、使用例を示します。
@POST @Path("/creditcard") @Counted( name="donation.counter", displayName="Number of donations", description = "Donations that were made using any method", monotonic=true, reusable=true) public String donateAmountViaCreditCard(@FormParam("amount") Long amount, @FormParam("card") String card) { if (processCard(card, amount)) return "Thanks for donating!"; return "Sorry, please try again."; }
@POST @Path("/debitcard") @Counted( name="donations.counter", displayName="Number of donations", description = "Donations that were made using any method", monotonic=true, reusable=true) public String donateAmountViaDebitCard(@FormParam("amount") Long amount, @FormParam("card") String card) { if (processDebitCard(card, amount)) return "Thanks for donating!"; return "Sorry, please try again."; }
CDI に関するヒント
mpMetrics-1.0 フィーチャーは CDI に依存しています。CDI 注入は、MetricRegistry を注入したい場合、または、メトリック・タイプの 1 つをフィールドに注入したい場合に常に使用されます。CDI インターセプターは、タイプ、コンストラクター、またはメソッドに対して、@Timed、@Counted、@Metered、および @Gauge メトリックを適用するために使用されます。
- 明示的 Bean アーカイブの使用
- アプリケーション始動の効率性のため、Bean アーカイブに beans.xml ファイルがあることを確認してください。Web アプリケーションを作成している場合、beans.xml ファイルは WEB-INF ディレクトリー内に置く必要があります。EJB モジュールまたは JAR ファイルを作成している場合、beans.xml ファイルは META-INF ディレクトリー内に置く必要があります。アプリケーション内で CDI の暗黙的 Bean アーカイブ機能を回避することによって、始動時に Bean アーカイブのスキャンを回避するように Liberty サーバーを調整できます。
- インターセプター @Timed、@Counted、@Metered、@Gauge の制限事項
- CDI は、Liberty にインターセプターとして実装される、メトリック・アノテーション用の Java インターセプターを利用します。@Timed アノテーション、@Counted アノテーション、@Metered アノテーション、および @Gauge アノテーションは、インターセプターを使用することによって実装されます。インターセプターは、コード・パス上で開始できるプロキシーを作成することができる (その後、そのプロキシーはインターセプター・アノテーションのあるコンストラクターまたはメソッドを呼び出すことができる) という機能に依存しています。メトリックが更新される場所はインターセプター・コードです。どういう種類のコードがプロキシー可能なのかと、どこでインターセプター・アノテーションを使用できるのかについて、制限があります。CDI で定義されているように、プロキシー可能な管理 Bean のみをインターセプターと共に使用できます。プロキシーは、 プロキシーする対象の Bean のサブクラスとして実装されます。あるクラスが確実にプロキシー可能であるようにするには、そのクラスは final とマークされていてはならず、非プライベートで引数なしのコンストラクターでなければなりません。
- 注入に関する制限事項
- MetricRegistry などのタイプをクラスに注入 (@Inject) するには、そのクラスが CDI によって管理されている必要があります。以下の JAX-RS 例では、
someclass.setMetricsRegistry が CDI によって自動的に呼び出されています。
クラスは以下の例では呼び出されません。@Path("/myPath") public class MyService { // lifecyle of Someclass will be managed by CDI @Inject Someclass someclass; ... } public class Someclass { // Someclass was injected and is managed by CDI and therefore can use @Inject // setMetricRegistry will be called before the first method call to Someclass @Inject public void setMetricRegistry(final MetricRegistry metrics) { } }
@Path("/myPath") public class MyService { // CDI doesn't manage lifecycle of Someclass Someclass someclass = new Someclass(); ... } public class Someclass { // This method will never be called by CDI since Someclass was not created with CDI @Inject public void setMetricRegistry(MetricRegistry metrics) { } }