使用 Micrometer 收集指标
创建一个应用程序,使用 Micrometer 指标库来收集运行时、扩展和应用程序指标,并将它们作为 Prometheus (OpenMetrics) 端点暴露出来。
本文档是 Quarkus 可观察性参考指南的一部分,该指南介绍了此组件和其他可观察性相关组件。
先决条件
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
解决方案
我们建议您按照步骤逐步创建应用程序,但如果您愿意,也可以直接跳到解决方案。您可以选择:
-
克隆 git 仓库:
git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或者 -
下载一个 归档文件。
解决方案位于 micrometer-quickstart
目录中。
1. 创建 Maven 项目
使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数用双引号括起来,例如"-DprojectArtifactId=micrometer-quickstart"
此命令生成一个 Maven 项目,该项目导入 micrometer-registry-prometheus
扩展作为依赖项。此扩展将加载核心 micrometer
扩展,以及支持 Prometheus 所需的额外库依赖项。
为了保持向后兼容性,该扩展使用 Micrometer 1.13+ 的 Prometheus 客户端 v0.x,而不是它们的默认 v1.x 客户端。 |
2. 创建 REST 端点
我们首先添加一个简单的端点来计算素数。
package org.acme.micrometer;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
@Path("/example")
@Produces("text/plain")
public class ExampleResource {
@GET
@Path("prime/{number}")
public String checkIfPrime(@PathParam("number") long number) {
if (number < 1) {
return "Only natural numbers can be prime numbers.";
}
if (number == 1) {
return number + " is not prime.";
}
if (number == 2 || number % 2 == 0) {
return number + " is not prime.";
}
if (testPrimeNumber(number)) {
return number + " is prime.";
} else {
return number + " is not prime.";
}
}
protected boolean testPrimeNumber(long number) {
for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
if (number % i == 0) {
return false;
}
}
return true;
}
}
在开发模式下启动您的应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
2.1. 查看自动生成的指标
Micrometer 扩展会自动记录 HTTP 服务器请求的时间。
让我们使用 curl
(或浏览器) 访问我们的端点几次
curl https://:8080/example/prime/256
curl https://:8080/example/prime/7919
Micrometer Prometheus MeterRegistry 扩展创建一个端点,我们可以使用它来观察收集的指标。让我们看看已经收集了哪些指标
curl https://:8080/q/metrics
在输出中查找 http_server_requests_seconds_count
、http_server_requests_seconds_sum
和 http_server_requests_seconds_max
。
为请求 URI、HTTP 方法 (GET、POST 等)、状态码 (200、302、404 等) 和更通用的结果字段添加维度标签。您应该找到类似这样的内容
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 2.0
http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896
# HELP http_server_requests_seconds_max
# TYPE http_server_requests_seconds_max gauge
http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896
#
指标是懒加载的,在访问端点之前,通常不会看到任何数据。 |
默认情况下,指标使用 Prometheus 格式 application/openmetrics-text
导出,您可以通过将 Accept
请求标头指定为 text/plain
(curl -H "Accept: text/plain" localhost:8080/q/metrics/
) 来恢复为以前的格式。
3. 注入 MeterRegistry
要注册 meter,您需要引用由 Micrometer 扩展配置和维护的 MeterRegistry
。
MeterRegistry
可以按如下方式注入到您的应用程序中
private final MeterRegistry registry;
ExampleResource(MeterRegistry registry) {
this.registry = registry;
}
4. 添加一个计数器
计数器用于测量仅增加的值。
让我们添加一个计数器,跟踪我们测试一个数字以查看它是否为素数的频率。我们将添加一个维度标签(也称为属性或标记),这将允许我们以不同的方式聚合此计数器值。
@GET
@Path("prime/{number}")
public String checkIfPrime(@PathParam("number") long number) {
if (number < 1) {
registry.counter("example.prime.number", "type", "not-natural") (1)
.increment(); (2)
return "Only natural numbers can be prime numbers.";
}
if (number == 1) {
registry.counter("example.prime.number", "type", "one") (1)
.increment(); (2)
return number + " is not prime.";
}
if (number == 2 || number % 2 == 0) {
registry.counter("example.prime.number", "type", "even") (1)
.increment(); (2)
return number + " is not prime.";
}
if (testPrimeNumber(number)) {
registry.counter("example.prime.number", "type", "prime") (1)
.increment(); (2)
return number + " is prime.";
} else {
registry.counter("example.prime.number", "type", "not-prime") (1)
.increment(); (2)
return number + " is not prime.";
}
}
1 | 查找或创建一个名为 example.prime.number 的计数器,它具有一个带有指定值的 type 标签。 |
2 | 增加该计数器。 |
4.1. 查看收集的指标
如果您没有在开发模式下运行 Quarkus,请再次启动它
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
尝试以下序列,并在纯文本输出中查找 example_prime_number_total
。
请注意,当 Micrometer 将 Prometheus 命名约定应用于原始指定的计数器名称 example.prime.number
时,会添加 _total
后缀。
curl https://:8080/example/prime/-1
curl https://:8080/example/prime/0
curl https://:8080/example/prime/1
curl https://:8080/example/prime/2
curl https://:8080/example/prime/3
curl https://:8080/example/prime/15
curl https://:8080/q/metrics
请注意,对于 example_prime_number_total
和 type
值的每个唯一组合,都有一个测量值。
查看此计数器生成的维度数据,您可以计数
-
检查负数的频率:
type="not-natural"
-
检查数字 1 的频率:
type="one"
-
检查偶数的频率:
type="even"
-
检查素数的频率:
type="prime"
-
检查非素数的频率:
type="not-prime"
您还可以通过将所有这些值聚合在一起,来计算检查数字的频率(通常)。
5. 添加一个计时器
计时器是测量持续时间的专门抽象。让我们添加一个计时器来测量确定数字是否为素数所需的时间。
@GET
@Path("prime/{number}")
public String checkIfPrime(@PathParam("number") long number) {
if (number < 1) {
registry.counter("example.prime.number", "type", "not-natural") (1)
.increment(); (2)
return "Only natural numbers can be prime numbers.";
}
if (number == 1) {
registry.counter("example.prime.number", "type", "one") (1)
.increment(); (2)
return number + " is not prime.";
}
if (number == 2 || number % 2 == 0) {
registry.counter("example.prime.number", "type", "even") (1)
.increment(); (2)
return number + " is not prime.";
}
if (timedTestPrimeNumber(number)) { (3)
registry.counter("example.prime.number", "type", "prime") (1)
.increment(); (2)
return number + " is prime.";
} else {
registry.counter("example.prime.number", "type", "not-prime") (1)
.increment(); (2)
return number + " is not prime.";
}
}
protected boolean timedTestPrimeNumber(long number) {
Timer.Sample sample = Timer.start(registry); (4)
boolean result = testPrimeNumber(number); (5)
sample.stop(registry.timer("example.prime.number.test", "prime", result + "")); (6)
return result;
}
1 | 查找或创建一个名为 example.prime.number 的计数器,它具有一个带有指定值的 type 标签。 |
2 | 增加该计数器。 |
3 | 调用一个包装原始 testPrimeNumber 方法的方法。 |
4 | 创建一个 Timer.Sample 来跟踪开始时间 |
5 | 调用要计时的函数,并存储布尔结果 |
6 | 使用指定的 ID 和一个带有结果值的 prime 标签查找或创建一个 Timer ,并记录 Timer.Sample 捕获的持续时间。 |
5.1. 查看收集的指标
如果您没有在开发模式下运行 Quarkus,请再次启动它
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
当为此计时器发出指标时,Micrometer 将应用 Prometheus 约定。具体来说,测量的持续时间将转换为秒,并且此单位包含在指标名称中。
尝试以下序列,并在纯文本输出中查找以下条目
-
example_prime_number_test_seconds_count
— 调用该方法的次数 -
example_prime_number_test_seconds_sum
— 所有方法调用的总持续时间 -
example_prime_number_test_seconds_max
— 衰减间隔内观察到的最大持续时间。如果该方法不经常调用,则此值将返回到 0。
curl https://:8080/example/prime/256
curl https://:8080/q/metrics
curl https://:8080/example/prime/7919
curl https://:8080/q/metrics
查看此计数器生成的维度数据,您可以使用总和和计数来计算确定数字是否为素数需要多长时间(平均)。使用维度标签,您或许能够了解素数的持续时间与非素数的持续时间是否存在显着差异。
6. 添加一个仪表
仪表测量一个可以随时间增加或减少的值,例如汽车上的速度表。仪表的值不会累积,而是在收集时观察。使用仪表来观察集合的大小,或者从函数返回的值。
private final LinkedList<Long> list = new LinkedList<>(); (1)
ExampleResource(MeterRegistry registry) {
this.registry = registry;
registry.gaugeCollectionSize("example.list.size", Tags.empty(), list); (2)
}
@GET
@Path("gauge/{number}")
public Long checkListSize(@PathParam("number") long number) { (3)
if (number == 2 || number % 2 == 0) {
// add even numbers to the list
list.add(number);
} else {
// remove items from the list for odd numbers
try {
number = list.removeFirst();
} catch (NoSuchElementException nse) {
number = 0;
}
}
return number;
}
1 | 定义一个将保存任意数字的列表。 |
2 | 注册一个将跟踪列表大小的仪表。 |
3 | 创建一个 REST 端点来填充列表。偶数添加到列表中,奇数从列表中删除一个元素。 |
6.1. 查看收集的指标
如果您没有在开发模式下运行 Quarkus,请再次启动它
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
然后尝试以下序列,并在纯文本输出中查找 example_list_size
curl https://:8080/example/gauge/1
curl https://:8080/example/gauge/2
curl https://:8080/example/gauge/4
curl https://:8080/q/metrics
curl https://:8080/example/gauge/6
curl https://:8080/example/gauge/5
curl https://:8080/example/gauge/7
curl https://:8080/q/metrics