为何在Datadog指标探索器中找不到Micrometer实现的计数器?
问题描述
我尝试用Micrometer集成Datadog,创建了heartBeatExecutionCounter和heartBeatSuccessCounter两个计数器指标,定义的指标名称分别是ep_heartbeat_job_execution_count和ep_heartbeat_job_success_count。但在Datadog指标探索器里搜不到这两个指标,查看日志发现无法向Datadog发送指标,报错信息如下:
2022-12-17 23:40:00,006 WARN [datadog-metrics-publisher] (DatadogMeterRegistry.java:149) - failed to send metrics to datadog java.lang.NoClassDefFoundError: io/micrometer/common/util/StringUtils at io.micrometer.datadog.DatadogNamingConvention.name(DatadogNamingConvention.java:62) ~[micrometer-registry-datadog-1.10.2.jar:1.10.2] at io.micrometer.core.instrument.Meter$Id.getConventionName(Meter.java:319) ~[micrometer-core-1.8.4.jar:1.8.4] at io.micrometer.core.instrument.MeterRegistry.getConventionName(MeterRegistry.java:253) ~[micrometer-core-1.8.4.jar:1.8.4] at io.micrometer.datadog.DatadogMeterRegistry.writeMetric(DatadogMeterRegistry.java:254) ~[micrometer-registry-datadog-1.10.2.jar:1.10.2] at io.micrometer.datadog.DatadogMeterRegistry.lambda$writeMeter$12(DatadogMeterRegistry.java:212) ~[micrometer-registry-datadog-1.10.2.jar:1.10.2] at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[?:?] at java.util.Collections$2.tryAdvance(Collections.java:4747) ~[?:?] at java.util.Collections$2.forEachRemaining(Collections.java:4755) ~[?:?] at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[?:?] at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[?:?] at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[?:?] at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[?:?] at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?] at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497) ~[?:?] at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:274) ~[?:?] at java.util.ArrayList$SubList$2.forEachRemaining(ArrayList.java:1511) ~[?:?] at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[?:?] at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[?:?] at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[?:?] at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?] at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[?:?] at io.micrometer.datadog.DatadogMeterRegistry.publish(DatadogMeterRegistry.java:138) ~[micrometer-registry-datadog-1.10.2.jar:1.10.2] at io.micrometer.core.instrument.push.PushMeterRegistry.publishSafely(PushMeterRegistry.java:52) ~[micrometer-core-1.8.4.jar:1.8.4] at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?] at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[?:?] at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[?:?] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?] at java.lang.Thread.run(Thread.java:834) ~[?:?]
实现代码
计数器定义
private MeterRegistry registry = DatadogUtils.getRegistry(); // 记录任务成功执行次数的计数器 private Counter heartBeatSuccessCounter = Counter.builder("ep_heartbeat_job_success_count") .description("EpHeartBeatJob任务成功执行的次数") .register(registry); // 记录任务总执行次数的计数器 private Counter heartBeatExecutionCounter = Counter.builder("ep_heartbeat_job_execution_count") .description("EpHeartBeatJob任务总执行次数") .register(registry);
计数器递增逻辑
@Override public void execute(JobExecutionContext context) { // 递增总执行次数计数器 heartBeatExecutionCounter.increment(); try { log.info("Job ** {} ** starting @ {}", context.getJobDetail().getKey().getName(), context.getFireTime()); if (isLocalEnv()) { epHeartBeatService.startTask(); } else { outageMillisPerDay += epHighAvailablityService.startTask(); log.info("Accumulated outage = {} milliseconds", outageMillisPerDay); calendar.setTime(context.getFireTime()); if (outageMillisPerDay > OUTAGE_MILLIS_ALLOWED_PER_WEEK) { //&& calendar.get(Calendar.DAY_OF_WEEK)==0) { throw new Exception("实际停机时间超过允许值"); } } // 递增成功执行次数计数器 heartBeatSuccessCounter.increment(); log.info("Job ** {} ** completed. Next job scheduled @ {}", context.getJobDetail().getKey().getName(), context.getNextFireTime()); log.info("===================================================="); } catch (Exception e) { log.error(e.getMessage(), e); } }
Datadog工具类
import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.datadog.DatadogConfig; import io.micrometer.datadog.DatadogMeterRegistry; public class DatadogUtils { private static DatadogConfig config = new DatadogConfig() { @Override public String apiKey() { return "my_key"; } @Override public String get(String key) {return null;}; }; private static final MeterRegistry registry = new DatadogMeterRegistry(config, Clock.SYSTEM); public static MeterRegistry getRegistry() { return registry; } }
build.gradle依赖配置
dependencies { ... implementation 'io.micrometer:micrometer-core:latest.release' }
请问我的实现哪里出了问题?
问题分析与解决
核心问题:版本不兼容+依赖缺失
从报错NoClassDefFoundError: io/micrometer/common/util/StringUtils可以定位两个关键问题:
- 版本不匹配:你使用的
micrometer-registry-datadog版本是1.10.2,但micrometer-core版本是1.8.4,两者版本差异过大。Micrometer从1.10.x版本开始,将StringUtils从io.micrometer.core.util迁移到了io.micrometer.common.util,低版本core包中没有这个类,导致运行时找不到。 - 依赖缺失:你的
build.gradle仅引入了micrometer-core,未显式引入micrometer-registry-datadog依赖,代码能编译可能是间接依赖引入了该包,但版本不符合要求。
解决步骤
1. 统一Micrometer版本并添加正确依赖
修改build.gradle,指定统一的Micrometer版本,同时显式引入Datadog注册表依赖,避免版本冲突:
def micrometerVersion = '1.10.2' dependencies { ... implementation "io.micrometer:micrometer-core:${micrometerVersion}" implementation "io.micrometer:micrometer-registry-datadog:${micrometerVersion}" }
不要使用latest.release,固定版本能有效避免版本不一致问题。
2. 完善Datadog配置
你的DatadogConfig实现中get(String key)方法直接返回null,会导致默认配置无法生效,建议修改为:
private static DatadogConfig config = new DatadogConfig() { @Override public String apiKey() { return "my_key"; } @Override public String get(String key) { // 继承默认配置 return DatadogConfig.super.get(key); } };
如果是国内环境或自定义端点,可添加URI配置:
@Override public String uri() { return "https://api.datadoghq.cn"; // 海外环境用https://api.datadoghq.com }
3. 验证指标发送
重启应用后,触发任务执行使计数器递增,等待Datadog默认的1分钟上报周期,再去指标探索器搜索(注意Datadog会自动转换命名格式,可尝试搜索ep.heartbeat.job.success.count)。
内容的提问来源于stack exchange,提问作者Hofbr
相关产品推荐
相关产品推荐

