You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus Micrometer单个Counter使用不同标签键失效问题咨询

问题

我想在代码中复用同一个Counter名称,通过不同Tags标记不同状态,但尝试后未生效,也没找到相关限制文档。

我的Counter代码如下:

@ApplicationScoped
@RequiredArgsConstructor
public class MetricsService {   

    private final Counter.Builder userRequestCounter = Counter.builder("incoming_user_request");
    private final Counter.Builder userResponseCounter = Counter.builder("incoming_user_response");
    
    public void incrementRequestStart() {
        userRequestCounter.tags("state", "started").register(registry).increment();
    }
    
    public void incrementRequestDone(String statusCode) {
        userRequestCounter.tags("state", "done", "statusCode", statusCode).register(registry).increment();
    }

    public void incrementRequestFailure(String errorCode) {
        userRequestCounter.tags("state", "failure", "errorCode", errorCode).register(registry).increment();
    }
}

我还尝试直接注册并递增:

registry.counter("incoming_user_request", ..tags)

同样无效。

尽管调用了两个方法,但metrics端点仅显示一个条目:

TYPE incoming_user_request counter
HELP incoming_user_request
incoming_user_request_total{state="started"} 1.0

我期望能显示所有条目:

TYPE incoming_user_request counter
HELP incoming_user_request
incoming_user_request_total{state="started"} 2.0
incoming_user_request_total{state="done", statusCode="OK"} 1.0
incoming_user_request_total{state="failure", errorCode="500"} 1.0

请问该需求是否可实现?我希望使用单个Counter名称,而非为不同状态创建多个Counter。


解决方案

这个需求完全可以实现,问题出在你使用Counter Builder的方式上:

  1. 重复使用同一个Counter.Builder实例调用tags()和register()时,Builder是有状态的——第一次调用tags("state", "started")后,Builder的标签就被固定为该值,后续调用tags()只是覆盖原有标签,而非创建新维度。每次register()时,若同名且标签匹配的Counter已存在,会直接返回已有实例,导致后续递增操作全部加到第一个state="started"的Counter上。

  2. 正确做法是先定义带标签键的Counter模板,再根据不同标签值获取对应Counter实例:

修正后的代码示例

方式一:预定义标签键的模板式实现

@ApplicationScoped
@RequiredArgsConstructor
public class MetricsService {   
    private final Counter userRequestCounter;

    // 构造时初始化带所有标签键的Counter
    public MetricsService(MeterRegistry registry) {
        this.userRequestCounter = Counter.builder("incoming_user_request")
                .labelNames("state", "statusCode", "errorCode") // 预先声明所有可能的标签键
                .register(registry);
    }
    
    public void incrementRequestStart() {
        // 通过labelValues指定对应标签值,获取对应维度的Counter并递增
        userRequestCounter.labels("started", "", "").increment();
    }
    
    public void incrementRequestDone(String statusCode) {
        userRequestCounter.labels("done", statusCode, "").increment();
    }

    public void incrementRequestFailure(String errorCode) {
        userRequestCounter.labels("failure", "", errorCode).increment();
    }
}

方式二:直接通过Registry获取对应标签的Counter

@ApplicationScoped
@RequiredArgsConstructor
public class MetricsService {   
    private final MeterRegistry registry;
    
    public void incrementRequestStart() {
        registry.counter("incoming_user_request", "state", "started").increment();
    }
    
    public void incrementRequestDone(String statusCode) {
        registry.counter("incoming_user_request", "state", "done", "statusCode", statusCode).increment();
    }

    public void incrementRequestFailure(String errorCode) {
        registry.counter("incoming_user_request", "state", "failure", "errorCode", errorCode).increment();
    }
}

关键说明

  • 对于Prometheus这类监控系统,同一指标名称下的不同标签组合会被视为独立时间序列,这正是你需要的多维度统计效果。
  • 保持标签键的一致性:同一指标下的标签键尽量统一,若部分场景不需要某标签,可根据实际需求调整标签设计(示例中为空字符串是为了匹配你的原始需求)。
  • 直接调用registry.counter()时,底层会自动复用已存在的同标签组合Counter实例,无需担心重复创建问题。

内容的提问来源于stack exchange,提问作者Bogdan Maceasa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:35:26