マイクロメーターのクイックガイド
1. 前書き
Micrometerprovides a simple facade over the instrumentation clients for a number of popular monitoring systems.現在、次の監視システムをサポートしています:Atlas、Datadog、Graphite、Ganglia、Influx、JMX、Prometheus。
この記事では、Micrometerの基本的な使用法とSpringとの統合について紹介します。
簡単にするために、ほとんどのユースケースを示す例としてMicrometerAtlasを取り上げます。
2. メーベン依存
まず、pom.xmlに次の依存関係を追加しましょう。
io.micrometer
micrometer-registry-atlas
0.12.0.RELEASE
最新バージョンはhereで見つけることができます。
3. MeterRegistry
マイクロメータでは、MeterRegistryはメータの登録に使用されるコアコンポーネントです。 レジストリを反復処理し、各メーターのメトリックをさらに処理して、メトリックとそのディメンション値の組み合わせを使用してバックエンドで時系列を生成できます。
レジストリの最も単純な形式はSimpleMeterRegistryです。 ただし、ほとんどの場合、監視システム用に明示的に設計されたMeterRegistryを使用する必要があります。 Atlasの場合、AtlasMeterRegistryです。
CompositeMeterRegistryを使用すると、複数のレジストリを追加できます。 サポートされているさまざまな監視システムにアプリケーションメトリックを同時に公開するソリューションを提供します。
複数のプラットフォームにデータをアップロードするために必要なMeterRegistryを追加できます。
CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry();
SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry();
AtlasMeterRegistry atlasMeterRegistry
= new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM);
compositeRegistry.add(oneSimpleMeter);
compositeRegistry.add(atlasMeterRegistry);
Micrometerには静的なグローバルレジストリサポートがあります:Metrics.globalRegistry。 また、このグローバルレジストリに基づく静的ビルダーのセットが提供され、Metricsでメーターを生成します。
@Test
public void givenGlobalRegistry_whenIncrementAnywhere_thenCounted() {
class CountedObject {
private CountedObject() {
Metrics.counter("objects.instance").increment(1.0);
}
}
Metrics.addRegistry(new SimpleMeterRegistry());
Metrics.counter("objects.instance").increment();
new CountedObject();
Optional counterOptional = Metrics.globalRegistry
.find("objects.instance").counter();
assertTrue(counterOptional.isPresent());
assertTrue(counterOptional.get().count() == 2.0);
}
4. TagsおよびMeters
4.1. Tags
Meterの識別子は、名前とタグで構成されます。 It is suggested that we should follow a naming convention that separates words with a dot, to help guarantee portability of metric names across multiple monitoring systems.
Counter counter = registry.counter("page.visitors", "age", "20s");
Tagsは、値について推論するためにメトリックをスライスするために使用できます。 上記のコードでは、page.visitorsはメーターの名前であり、そのタグはage=20sです。 この場合、カウンターは20〜30歳のページへの訪問者をカウントするためのものです。
大規模なシステムでは、共通のタグをレジストリに追加できます。たとえば、メトリックは特定の地域からのものです。
registry.config().commonTags("region", "ua-east");
4.2. Counter
Counterは、アプリケーションの指定されたプロパティのカウントのみを報告します。 流暢なビルダーまたは任意のMetricRegistryのヘルパーメソッドを使用してカスタムカウンターを作成できます。
Counter counter = Counter
.builder("instance")
.description("indicates instance count of the object")
.tags("dev", "performance")
.register(registry);
counter.increment(2.0);
assertTrue(counter.count() == 2);
counter.increment(-1);
assertTrue(counter.count() == 2);
上記のスニペットからわかるように、カウンターを1つ減らしようとしましたが、we can only increment the counter monotonically by a fixed positive amount.
4.3. Timers
システム内のイベントの待機時間や頻度を測定するには、Timersを使用できます。 Timerは、少なくとも特定の時系列の合計時間とイベント数を報告します。
たとえば、数秒続くアプリケーションイベントを記録できます。
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = registry.timer("app.event");
timer.record(() -> {
try {
TimeUnit.MILLISECONDS.sleep(1500);
} catch (InterruptedException ignored) { }
});
timer.record(3000, MILLISECONDS);
assertTrue(2 == timer.count());
assertTrue(4510 > timer.totalTime(MILLISECONDS)
&& 4500 <= timer.totalTime(MILLISECONDS));
長時間実行されるイベントを記録するには、LongTaskTimerを使用します。
SimpleMeterRegistry registry = new SimpleMeterRegistry();
LongTaskTimer longTaskTimer = LongTaskTimer
.builder("3rdPartyService")
.register(registry);
long currentTaskId = longTaskTimer.start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException ignored) { }
long timeElapsed = longTaskTimer.stop(currentTaskId);
assertTrue(timeElapsed / (int) 1e9 == 2);
4.4. Gauge
ゲージはメーターの現在の値を示します。
他のメーターとは異なり、Gaugesは観測された場合にのみデータを報告する必要があります。 Gaugesは、キャッシュ、コレクションなどの統計を監視するときに役立ちます。
SimpleMeterRegistry registry = new SimpleMeterRegistry();
List list = new ArrayList<>(4);
Gauge gauge = Gauge
.builder("cache.size", list, List::size)
.register(registry);
assertTrue(gauge.value() == 0.0);
list.add("1");
assertTrue(gauge.value() == 1.0);
4.5. DistributionSummary
イベントの配布と簡単な要約は、DistributionSummaryによって提供されます。
SimpleMeterRegistry registry = new SimpleMeterRegistry();
DistributionSummary distributionSummary = DistributionSummary
.builder("request.size")
.baseUnit("bytes")
.register(registry);
distributionSummary.record(3);
distributionSummary.record(4);
distributionSummary.record(5);
assertTrue(3 == distributionSummary.count());
assertTrue(12 == distributionSummary.totalAmount());
さらに、DistributionSummaryおよびTimersは、分位数によって強化できます。
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = Timer.builder("test.timer")
.quantiles(WindowSketchQuantiles
.quantiles(0.3, 0.5, 0.95)
.create())
.register(registry);
上記のスニペットでは、タグquantile=0.3、quantile=0.5、およびquantile=0.95の3つのゲージがレジストリで使用可能になり、95%、50%、および30%の観測値が下がる値を示します。それぞれ。
これらの変位値の動作を確認するために、次のレコードを追加しましょう。
timer.record(2, TimeUnit.SECONDS);
timer.record(2, TimeUnit.SECONDS);
timer.record(3, TimeUnit.SECONDS);
timer.record(4, TimeUnit.SECONDS);
timer.record(8, TimeUnit.SECONDS);
timer.record(13, TimeUnit.SECONDS);
次に、これら3つの変位値Gaugesの値を抽出することで検証できます。
List quantileGauges = registry.getMeters().stream()
.filter(m -> m.getType().name().equals("Gauge"))
.map(meter -> (Gauge) meter)
.collect(Collectors.toList());
assertTrue(3 == quantileGauges.size());
Map quantileMap = extractTagValueMap(registry, Type.Gauge, 1e9);
assertThat(quantileMap, allOf(
hasEntry("quantile=0.3",2),
hasEntry("quantile=0.5", 3),
hasEntry("quantile=0.95", 8)));
さらに、Micrometerはヒストグラムもサポートしています。
DistributionSummary hist = DistributionSummary
.builder("summary")
.histogram(Histogram.linear(0, 10, 5))
.register(registry);
変位値と同様に、いくつかのレコードを追加した後、ヒストグラムが計算を非常にうまく処理していることがわかります。
Map histograms = extractTagValueMap(registry, Type.Counter, 1.0);
assertThat(histograms, allOf(
hasEntry("bucket=0.0", 0),
hasEntry("bucket=10.0", 2),
hasEntry("bucket=20.0", 2),
hasEntry("bucket=30.0", 1),
hasEntry("bucket=40.0", 1),
hasEntry("bucket=Infinity", 0)));
一般に、ヒストグラムは個別のバケットでの直接比較を説明するのに役立ちます。 ヒストグラムは時間スケールすることもでき、バックエンドサービスの応答時間を分析するのに非常に役立ちます。
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = Timer
.builder("timer")
.histogram(Histogram.linearTime(TimeUnit.MILLISECONDS, 0, 200, 3))
.register(registry);
//...
assertThat(histograms, allOf(
hasEntry("bucket=0.0", 0),
hasEntry("bucket=2.0E8", 1),
hasEntry("bucket=4.0E8", 1),
hasEntry("bucket=Infinity", 3)));
5. バインダー
マイクロメータには、JVM、キャッシュ、ExecutorService、およびロギングサービスを監視するための複数の組み込みバインダーがあります。
JVMとシステムの監視に関しては、クラスローダーメトリック(ClassLoaderMetrics)、JVMメモリプール(JvmMemoryMetrics)とGCメトリック(JvmGcMetrics)、スレッドとCPU使用率(%)を監視できます。 (t3)s、ProcessorMetrics)。
キャッシュモニタリング(現在、Guava、EhCache、Hazelcast、およびCaffeineのみがサポートされています)は、GuavaCacheMetrics、EhCache2Metrics、HazelcastCacheMetrics、およびCaffeineCacheMetricsを使用してインストルメント化することでサポートされます。 また、ログバックサービスを監視するために、LogbackMetricsを任意の有効なレジストリにバインドできます。
new LogbackMetrics().bind(registry);
上記のバインダーの使用法はLogbackMetricsと非常によく似ており、すべてかなり単純なので、ここではこれ以上詳しく説明しません。
6. 春の統合
Spring Boot Actuator provides dependency management and auto-configuration for Micrometer. Spring Boot 2.0 /1.xおよびSpringFramework 5.0 /4.xでサポートされるようになりました。
次の依存関係が必要になります(最新バージョンはhereにあります)。
io.micrometer
micrometer-spring-legacy
0.12.0.RELEASE
既存のコードをさらに変更することなく、MicrometerでSpringサポートを有効にしました。 SpringアプリケーションのJVMメモリメトリックは、グローバルレジストリに自動的に登録され、デフォルトのアトラスエンドポイントhttp://localhost:7101/api/v1/publishに公開されます。
spring.metrics.atlas.*で始まる、メトリックのエクスポート動作を制御するために使用できる構成可能なプロパティがいくつかあります。 AtlasConfigをチェックして、Atlasパブリッシングの構成プロパティの完全なリストを確認してください。
さらにメトリックをバインドする必要がある場合は、それらを@Beanとしてアプリケーションコンテキストに追加するだけです。
JvmThreadMetricsが必要だとします。
@Bean
JvmThreadMetrics threadMetrics(){
return new JvmThreadMetrics();
}
Webモニタリングに関しては、アプリケーション内のすべてのエンドポイントに対して自動構成されますが、構成プロパティspring.metrics.web.autoTimeServerRequestsを介して管理できます。
デフォルトの実装では、エンドポイントのメトリックの4つのディメンション(HTTP要求メソッド、HTTP応答コード、エンドポイントURI、および例外情報)が提供されます。
リクエストが応答されると、リクエストメソッドに関連するメトリック(GET、POSTなど)がAtlasで公開されます。
Atlas Graph APIを使用すると、さまざまなメソッドの応答時間を比較するグラフを生成できます。
デフォルトでは、20x、30x、40x、50xの応答コードも報告されます。
異なるURIを比較することもできます:
または、例外メトリックを確認します。
コントローラクラスまたは特定のエンドポイントメソッドで@Timedを使用して、メトリックのタグ、長いタスク、分位数、およびパーセンタイルをカスタマイズすることもできることに注意してください。
@RestController
@Timed("people")
public class PeopleController {
@GetMapping("/people")
@Timed(value = "people.all", longTask = true)
public List listPeople() {
//...
}
}
上記のコードに基づいて、Atlasエンドポイントhttp://localhost:7101/api/v1/tags/nameをチェックすると、次のタグを確認できます。
["people", "people.all", "jvmBufferCount", ... ]
Micrometerは、Spring Boot 2.0で導入された関数Webフレームワークでも機能します。 メトリックは、RouterFunctionをフィルタリングすることで有効にできます。
RouterFunctionMetrics metrics = new RouterFunctionMetrics(registry);
RouterFunctions.route(...)
.filter(metrics.timer("server.requests"));
データソースおよびスケジュールされたタスクからのメトリックも収集できます。 詳細については、official documentationを確認してください。
7. 結論
この記事では、メトリックファサードMicrometerを紹介しました。 共通のセマンティクスの下で複数の監視システムを抽象化してサポートすることにより、このツールは異なる監視プラットフォーム間の切り替えを非常に簡単にします。
いつものように、この記事の完全な実装コードはover on Githubにあります。