Spring Micrometer的@Counted注解能否忽略指定标签,仅按错误码统计指标?
先直接给结论:原生的@Counted注解没办法直接实现你要的效果——因为Micrometer里的指标标签是用来区分不同时间序列的,每个error_code+unique_id的组合都会被当成独立的指标,所以每个unique_id都会单独计数,不会自动合并。
先理清楚你的需求:你现在的注解同时加了两个标签,所以每个不同的unique_id都会生成一条独立的指标。而你想要的是同一个error_code下,不管unique_id是什么,计数都累加。下面给你几个可行的解决方案:
方案1:移除unique_id标签(最简单直接)
如果你的核心需求就是按error_code聚合计数,unique_id只是用来追踪单个请求而非聚合维度,那直接修改@Counted注解,去掉unique_id的标签配置就好:
@Counted(name = "my_metric", labels = {"error_code:$0"})
这样不管传入什么unique_id,指标都会按error_code来聚合,最终你会得到:
my_metric_total{error_code="test-1"} 2.0
要是需要记录unique_id的信息,用日志来记录就够了——指标标签应该用来做低基数的聚合维度,像unique_id这种高基数的值会导致指标数量爆炸,这是监控领域的大忌,Micrometer本身也不推荐这么用。
方案2:自定义MeterFilter合并标签(适合需要统一处理指标的场景)
如果你必须在代码里保留unique_id的标签配置,但又想让指标最终按error_code聚合,可以通过自定义MeterFilter来拦截并修改指标:
创建一个自定义的过滤器组件:
@Component public class UniqueIdAggregationFilter implements MeterFilter { @Override public Meter.Id map(Meter.Id id) { // 只处理我们关心的my_metric指标 if ("my_metric_total".equals(id.getName())) { // 过滤掉unique_id标签,只保留error_code List<Tag> filteredTags = id.getTags().stream() .filter(tag -> !"unique_id".equals(tag.getKey())) .collect(Collectors.toList()); return id.withTags(filteredTags); } // 其他指标保持原样 return id; } }
这个过滤器会在指标注册到MeterRegistry时移除unique_id标签,这样所有相同error_code的请求都会被合并到同一个指标里计数累加。不过这样最终的指标里就看不到unique_id了,和方案1的效果一致。
方案3:自定义CountedAspect(最灵活的控制)
如果你的场景更复杂,比如某些情况下需要保留unique_id标签,某些情况下需要聚合,那可以自定义CountedAspect来覆盖默认的计数逻辑:
@Aspect @Component public class CustomCountedAspect extends CountedAspect { public CustomCountedAspect(MeterRegistry meterRegistry) { super(meterRegistry); } @Override protected void increment(Counted counted, String[] args) { // 只取第一个参数作为error_code,忽略第二个unique_id参数 String errorCode = args[0]; // 创建只带error_code标签的计数器并累加 Counter counter = Counter.builder(counted.name()) .tag("error_code", errorCode) .register(getMeterRegistry()); counter.increment(); } }
这样不管你原来的@Counted注解里怎么配置unique_id标签,自定义的切面都会忽略它,只按error_code来创建计数器并累加计数。
最后提个醒
一定要注意:高基数标签(比如每个请求都不一样的unique_id)会严重增加Prometheus的存储和查询压力,甚至可能导致监控系统崩溃。所以除非有特别明确的需求,否则别把这类高基数的值作为指标标签,用日志来追踪单个请求的唯一ID才是更合理的做法。
内容的提问来源于stack exchange,提问作者Estarossa

