仪表过滤器
你可以为每个注册表配置计量过滤器,这些过滤器让你更好地控制计量器的注册方式、注册时间以及它们发出的统计数据类型。计量过滤器有三个基本功能:
-
拒绝(或接受)仪表的注册。
-
转换仪表 ID(例如,更改名称、添加或删除标签,以及更改描述或基本单位)。
-
配置某些仪表类型的分布统计信息。
MeterFilter
的实现以编程方式添加到注册表中:
registry.config()
.meterFilter(MeterFilter.ignoreTags("too.much.information"))
.meterFilter(MeterFilter.denyNameStartsWith("jvm"));
Meter 过滤器按顺序应用,转换或配置 Meter 的结果会被串联起来。
拒绝或接受仪表
接受或拒绝过滤器的详细形式为:
new MeterFilter() {
@Override
public MeterFilterReply accept(Meter.Id id) {
if(id.getName().contains("test")) {
return MeterFilterReply.DENY;
}
return MeterFilterReply.NEUTRAL;
}
}
MeterFilterReply
有三种可能的状态:
-
DENY
:不允许此计量器注册。当你尝试向注册表注册一个计量器时,如果过滤器返回DENY
,注册表将返回该计量器的 NOOP 版本(例如NoopCounter
或NoopTimer
)。你的代码可以继续与 NOOP 计量器交互,但记录到其中的任何内容都会立即被丢弃,且开销最小。 -
NEUTRAL
:如果没有其他计量器过滤器返回DENY
,则计量器的注册正常进行。 -
ACCEPT
:如果过滤器返回ACCEPT
,则计量器会立即注册,而无需询问任何其他过滤器的接受方法。
便捷方法
MeterFilter
提供了几个方便的静态构建器,用于创建拒绝和接受类型的过滤器:
-
accept()
: 接受所有指标,覆盖后续任何过滤器的决策。 -
accept(Predicate<Meter.Id>)
: 接受与谓词匹配的任何指标。 -
acceptNameStartsWith(String)
: 接受具有匹配前缀的所有指标。 -
deny()
: 拒绝所有指标,覆盖后续任何过滤器的决策。 -
denyNameStartsWith(String)
: 拒绝具有匹配前缀的所有指标。Micrometer 提供的所有MeterBinder
实现都具有常见前缀的名称,以便在 UI 中轻松进行分组可视化,同时也便于通过前缀批量禁用或启用。例如,您可以使用MeterFilter.denyNameStartsWith("jvm")
来拒绝所有 JVM 指标。 -
deny(Predicate<Meter.Id>)
: 拒绝与谓词匹配的任何指标。 -
maximumAllowableMetrics(int)
: 当注册表达到一定数量的指标后,拒绝任何其他指标。 -
maximumAllowableTags(String meterNamePrefix, String tagKey, int maximumTagValues, MeterFilter onMaxReached)
: 对匹配系列生成的标签数量设置上限。
白名单仅针对某一组指标是监控系统中的一个特别常见的用例,尤其是对于昂贵的系统。这可以通过静态调用来实现:
denyUnless(Predicate<Meter.Id>)
:拒绝所有不匹配谓词的仪表。
链式拒绝接受仪表
Meter 过滤器按照它们在注册表上配置的顺序应用,因此可以堆叠拒绝和接受过滤器以实现更复杂的规则:
registry.config()
.meterFilter(MeterFilter.acceptNameStartsWith("http"))
.meterFilter(MeterFilter.deny()); 1
通过将两个过滤器叠加在一起,实现了另一种形式的白名单机制。在这个注册表中,只允许存在 http
相关的指标。
转换指标
以下示例展示了一个转换过滤器:
new MeterFilter() {
@Override
public Meter.Id map(Meter.Id id) {
if(id.getName().startsWith("test")) {
return id.withName("extra." + id.getName()).withTag("extra.tag", "value");
}
return id;
}
}
此过滤器会为名称以 test
开头的计量器添加一个名称前缀,并根据条件添加一个额外的标签。
MeterFilter
的 map
实现应该是“静态的”
id
参数是 MeterFilter
生命周期中唯一会变化的动态输入,它们应该依赖于这个参数。对于需要动态行为的用例,例如基于请求上下文定义标签等,应该在 instrumentation 本身中实现,而不是在 MeterFilter
中实现。例如,可以参考 MongoMetricsCommandListener
以及它通过构造函数参数接收的 MongoCommandTagsProvider
和默认实现 DefaultMongoCommandTagsProvider
。另请参阅 ObservationFilter,它允许动态实现。
便利方法
MeterFilter
为许多常见的转换场景提供了便捷的构建器:
-
commonTags(Iterable<Tag>)
:为所有指标添加一组标签。为应用程序名称、主机、区域等添加常用标签是非常推荐的做法。 -
ignoreTags(String…)
:从每个计量器中删除匹配的标签键。当某个标签的基数过高,开始对监控系统产生压力或成本过高时,这个功能特别有用,尤其是在你无法快速更改所有检测点的情况下。 -
replaceTagValues(String tagKey, Function<String, String> replacement, String… exceptions)
:根据提供的映射替换所有匹配标签键的标签值。你可以使用这个功能通过将部分标签值映射到其他内容来减少标签的总基数。 -
renameTag(String meterNamePrefix, String fromTagKey, String toTagKey)
:重命名以给定前缀开头的每个指标的标签键。
配置分布统计
Timer
和 DistributionSummary
包含一组可选的分布统计信息(除了基本的计数、总和和最大值之外),你可以通过过滤器进行配置。这些分布统计信息包括预计算的百分位数、SLO(服务水平目标)和直方图。
new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
if (id.getName().startsWith(prefix)) {
return DistributionStatisticConfig.builder()
.publishPercentiles(0.9, 0.95)
.build()
.merge(config);
}
return config;
}
};
通常,你应该创建一个新的 DistributionStatisticConfig
,只包含你想要配置的部分,然后将其与输入配置进行 merge
。这样可以让你依赖于注册表提供的分布统计默认值,并且可以将多个过滤器链接在一起,每个过滤器配置分布统计的某一部分(例如,你可能希望为所有 HTTP 请求设置 100ms 的 SLO,但只在少数关键端点上启用百分位直方图)。
MeterFilter
提供了以下便捷构建器:
-
maxExpected(Duration/long)
: 控制从计时器或摘要中发送的百分位数直方图桶的上限。 -
minExpected(Duration/long)
: 控制从计时器或摘要中发送的百分位数直方图桶的下限。
Spring Boot 提供了基于属性的过滤器,用于通过名称前缀配置 SLOs(服务级别目标)、百分位数以及百分位直方图。