diff --git a/implementations/micrometer-registry-cloudwatch2/src/main/java/io/micrometer/cloudwatch2/CloudWatchMeterRegistry.java b/implementations/micrometer-registry-cloudwatch2/src/main/java/io/micrometer/cloudwatch2/CloudWatchMeterRegistry.java index ae078a81bd..e0b4bd52af 100644 --- a/implementations/micrometer-registry-cloudwatch2/src/main/java/io/micrometer/cloudwatch2/CloudWatchMeterRegistry.java +++ b/implementations/micrometer-registry-cloudwatch2/src/main/java/io/micrometer/cloudwatch2/CloudWatchMeterRegistry.java @@ -19,6 +19,8 @@ import io.micrometer.common.util.internal.logging.WarnThenDebugLogger; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.*; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.pause.PauseDetector; import io.micrometer.core.instrument.step.StepMeterRegistry; import io.micrometer.core.instrument.util.NamedThreadFactory; import org.jspecify.annotations.Nullable; @@ -33,9 +35,12 @@ import java.time.Instant; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.function.ToDoubleFunction; +import java.util.function.ToLongFunction; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; @@ -79,6 +84,11 @@ public class CloudWatchMeterRegistry extends StepMeterRegistry { private static final WarnThenDebugLogger tooManyTagsLogger = new WarnThenDebugLogger(CloudWatchMeterRegistry.class); + /** + * Cache of per-meter `publishAverage` flags captured when meters are created. + */ + private final Map publishAvgFlags = new ConcurrentHashMap<>(); + public CloudWatchMeterRegistry(CloudWatchConfig config, Clock clock, CloudWatchAsyncClient cloudWatchAsyncClient) { this(config, clock, cloudWatchAsyncClient, new NamedThreadFactory("cloudwatch-metrics-publisher")); } @@ -93,6 +103,27 @@ public CloudWatchMeterRegistry(CloudWatchConfig config, Clock clock, CloudWatchA start(threadFactory); } + @Override + protected Timer newTimer(Meter.Id id, DistributionStatisticConfig config, PauseDetector pauseDetector) { + publishAvgFlags.put(id, config.isPublishingAverage()); + return super.newTimer(id, config, pauseDetector); + } + + @Override + protected DistributionSummary newDistributionSummary(Meter.Id id, DistributionStatisticConfig config, + double scale) { + publishAvgFlags.put(id, config.isPublishingAverage()); + return super.newDistributionSummary(id, config, scale); + } + + @Override + protected FunctionTimer newFunctionTimer(Meter.Id id, T obj, ToLongFunction countFunction, + ToDoubleFunction totalTimeFunction, TimeUnit totalTimeFunctionUnit) { + // FunctionTimer does NOT get DistributionStatisticConfig → fallback to DEFAULT + publishAvgFlags.put(id, DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + return super.newFunctionTimer(id, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit); + } + @Override protected void publish() { boolean interrupted = false; @@ -193,10 +224,17 @@ Stream timerData(Timer timer) { .add(metricDatum(timer.getId(), "sum", getBaseTimeUnit().name(), timer.totalTime(getBaseTimeUnit()))); long count = timer.count(); metrics.add(metricDatum(timer.getId(), "count", StandardUnit.COUNT, (double) count)); + Boolean publishAvg = publishAvgFlags.getOrDefault(timer.getId(), + DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + if (count > 0) { - metrics.add(metricDatum(timer.getId(), "avg", getBaseTimeUnit().name(), timer.mean(getBaseTimeUnit()))); + if (publishAvg) { + metrics.add( + metricDatum(timer.getId(), "avg", getBaseTimeUnit().name(), timer.mean(getBaseTimeUnit()))); + } metrics.add(metricDatum(timer.getId(), "max", getBaseTimeUnit().name(), timer.max(getBaseTimeUnit()))); } + return metrics.build(); } @@ -206,10 +244,16 @@ Stream summaryData(DistributionSummary summary) { metrics.add(metricDatum(summary.getId(), "sum", summary.totalAmount())); long count = summary.count(); metrics.add(metricDatum(summary.getId(), "count", StandardUnit.COUNT, (double) count)); + Boolean publishAvg = publishAvgFlags.getOrDefault(summary.getId(), + DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + if (count > 0) { - metrics.add(metricDatum(summary.getId(), "avg", summary.mean())); + if (publishAvg) { + metrics.add(metricDatum(summary.getId(), "avg", summary.mean())); + } metrics.add(metricDatum(summary.getId(), "max", summary.max())); } + return metrics.build(); } @@ -247,9 +291,15 @@ Stream functionTimerData(FunctionTimer timer) { double count = timer.count(); metrics.add(metricDatum(timer.getId(), "count", StandardUnit.COUNT, count)); metrics.add(metricDatum(timer.getId(), "sum", sum)); + Boolean publishAvg = publishAvgFlags.getOrDefault(timer.getId(), + DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + if (count > 0) { - metrics.add(metricDatum(timer.getId(), "avg", timer.mean(getBaseTimeUnit()))); + if (publishAvg) { + metrics.add(metricDatum(timer.getId(), "avg", timer.mean(getBaseTimeUnit()))); + } } + return metrics.build(); } diff --git a/implementations/micrometer-registry-datadog/src/main/java/io/micrometer/datadog/DatadogMeterRegistry.java b/implementations/micrometer-registry-datadog/src/main/java/io/micrometer/datadog/DatadogMeterRegistry.java index dc58afa8ef..944872c78d 100644 --- a/implementations/micrometer-registry-datadog/src/main/java/io/micrometer/datadog/DatadogMeterRegistry.java +++ b/implementations/micrometer-registry-datadog/src/main/java/io/micrometer/datadog/DatadogMeterRegistry.java @@ -16,6 +16,8 @@ package io.micrometer.datadog; import io.micrometer.core.instrument.*; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.pause.PauseDetector; import io.micrometer.core.instrument.step.StepMeterRegistry; import io.micrometer.core.instrument.util.MeterPartition; import io.micrometer.core.instrument.util.NamedThreadFactory; @@ -33,7 +35,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.function.ToDoubleFunction; +import java.util.function.ToLongFunction; import java.util.stream.Stream; +import java.util.Objects; import static io.micrometer.core.instrument.util.StringEscapeUtils.escapeJson; import static java.util.stream.Collectors.joining; @@ -53,6 +58,11 @@ public class DatadogMeterRegistry extends StepMeterRegistry { private final HttpSender httpClient; + /** + * Cache of per-meter `publishAverage` settings captured at meter creation time. + */ + private final Map publishAvgFlags = new ConcurrentHashMap<>(); + /** * Metric names for which we have posted metadata concerning type and base unit */ @@ -104,6 +114,29 @@ public void start(ThreadFactory threadFactory) { super.start(threadFactory); } + @Override + protected Timer newTimer(Meter.Id id, DistributionStatisticConfig config, PauseDetector pauseDetector) { + publishAvgFlags.put(id, config.isPublishingAverage()); + return super.newTimer(id, config, pauseDetector); + } + + @Override + protected DistributionSummary newDistributionSummary(Meter.Id id, DistributionStatisticConfig config, + double scale) { + publishAvgFlags.put(id, config.isPublishingAverage()); + return super.newDistributionSummary(id, config, scale); + } + + @Override + protected FunctionTimer newFunctionTimer(Meter.Id id, T obj, ToLongFunction countFunction, + ToDoubleFunction totalTimeFunction, TimeUnit totalTimeFunctionUnit) { + // FunctionTimer never receives a DistributionStatisticConfig. + // So we fall back to DEFAULT.isPublishingAverage(). + + publishAvgFlags.put(id, DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + return super.newFunctionTimer(id, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit); + } + @Override protected void publish() { Map metadataToSend = new HashMap<>(); @@ -160,14 +193,23 @@ private Stream writeTimer(FunctionTimer timer, Map writeTimer(Timer timer, Map metadata) { @@ -175,14 +217,20 @@ private Stream writeTimer(Timer timer, Map metrics = Stream.builder(); Meter.Id id = timer.getId(); - metrics.add(writeMetric(id, "sum", wallTime, timer.totalTime(getBaseTimeUnit()), Statistic.TOTAL_TIME, null)); - metrics.add(writeMetric(id, "count", wallTime, (double) timer.count(), Statistic.COUNT, "occurrence")); - metrics.add(writeMetric(id, "avg", wallTime, timer.mean(getBaseTimeUnit()), Statistic.VALUE, null)); - metrics.add(writeMetric(id, "max", wallTime, timer.max(getBaseTimeUnit()), Statistic.MAX, null)); + metrics.add(writeMetric(id, "sum", wallTime, timer.totalTime(getBaseTimeUnit()), Statistic.TOTAL_TIME, null)); addToMetadataList(metadata, id, "sum", Statistic.TOTAL_TIME, null); + + metrics.add(writeMetric(id, "count", wallTime, (double) timer.count(), Statistic.COUNT, "occurrence")); addToMetadataList(metadata, id, "count", Statistic.COUNT, "occurrence"); - addToMetadataList(metadata, id, "avg", Statistic.VALUE, null); + + // publish avg only when enabled + if (publishAvgFlags.getOrDefault(id, DistributionStatisticConfig.DEFAULT.isPublishingAverage())) { + metrics.add(writeMetric(id, "avg", wallTime, timer.mean(getBaseTimeUnit()), Statistic.VALUE, null)); + addToMetadataList(metadata, id, "avg", Statistic.VALUE, null); + } + + metrics.add(writeMetric(id, "max", wallTime, timer.max(getBaseTimeUnit()), Statistic.MAX, null)); addToMetadataList(metadata, id, "max", Statistic.MAX, null); return metrics.build(); @@ -193,14 +241,20 @@ private Stream writeSummary(DistributionSummary summary, Map metrics = Stream.builder(); Meter.Id id = summary.getId(); - metrics.add(writeMetric(id, "sum", wallTime, summary.totalAmount(), Statistic.TOTAL, null)); - metrics.add(writeMetric(id, "count", wallTime, (double) summary.count(), Statistic.COUNT, "occurrence")); - metrics.add(writeMetric(id, "avg", wallTime, summary.mean(), Statistic.VALUE, null)); - metrics.add(writeMetric(id, "max", wallTime, summary.max(), Statistic.MAX, null)); + metrics.add(writeMetric(id, "sum", wallTime, summary.totalAmount(), Statistic.TOTAL, null)); addToMetadataList(metadata, id, "sum", Statistic.TOTAL, null); + + metrics.add(writeMetric(id, "count", wallTime, (double) summary.count(), Statistic.COUNT, "occurrence")); addToMetadataList(metadata, id, "count", Statistic.COUNT, "occurrence"); - addToMetadataList(metadata, id, "avg", Statistic.VALUE, null); + + // avg (only when enabled) + if (publishAvgFlags.getOrDefault(id, DistributionStatisticConfig.DEFAULT.isPublishingAverage())) { + metrics.add(writeMetric(id, "avg", wallTime, summary.mean(), Statistic.VALUE, null)); + addToMetadataList(metadata, id, "avg", Statistic.VALUE, null); + } + + metrics.add(writeMetric(id, "max", wallTime, summary.max(), Statistic.MAX, null)); addToMetadataList(metadata, id, "max", Statistic.MAX, null); return metrics.build(); diff --git a/implementations/micrometer-registry-humio/src/main/java/io/micrometer/humio/HumioMeterRegistry.java b/implementations/micrometer-registry-humio/src/main/java/io/micrometer/humio/HumioMeterRegistry.java index cee418ac87..4a7bb9c340 100644 --- a/implementations/micrometer-registry-humio/src/main/java/io/micrometer/humio/HumioMeterRegistry.java +++ b/implementations/micrometer-registry-humio/src/main/java/io/micrometer/humio/HumioMeterRegistry.java @@ -16,6 +16,7 @@ package io.micrometer.humio; import io.micrometer.core.instrument.*; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.core.instrument.distribution.HistogramSnapshot; import io.micrometer.core.instrument.step.StepMeterRegistry; import io.micrometer.core.instrument.util.DoubleFormat; @@ -32,8 +33,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.function.ToDoubleFunction; +import java.util.function.ToLongFunction; import static io.micrometer.core.instrument.util.StringEscapeUtils.escapeJson; import static java.util.stream.Collectors.joining; @@ -52,6 +56,11 @@ public class HumioMeterRegistry extends StepMeterRegistry { private final Logger logger = LoggerFactory.getLogger(HumioMeterRegistry.class); + /** + * Cache of per-meter `publishAverage` flags captured when meters are created. + */ + private final Map publishAvgFlags = new ConcurrentHashMap<>(); + private final HumioConfig config; private final HttpSender httpClient; @@ -126,6 +135,32 @@ protected void publish() { } } + @Override + protected Timer newTimer(Meter.Id id, io.micrometer.core.instrument.distribution.DistributionStatisticConfig config, + io.micrometer.core.instrument.distribution.pause.PauseDetector pauseDetector) { + + publishAvgFlags.put(id, config.isPublishingAverage()); + return super.newTimer(id, config, pauseDetector); + } + + @Override + protected DistributionSummary newDistributionSummary(Meter.Id id, + io.micrometer.core.instrument.distribution.DistributionStatisticConfig config, double scale) { + + publishAvgFlags.put(id, config.isPublishingAverage()); + return super.newDistributionSummary(id, config, scale); + } + + @Override + protected FunctionTimer newFunctionTimer(Meter.Id id, T obj, ToLongFunction countFunction, + ToDoubleFunction totalTimeFunction, TimeUnit totalTimeFunctionUnit) { + + // FunctionTimer has NO DistributionStatisticConfig → fallback to DEFAULT + publishAvgFlags.put(id, + io.micrometer.core.instrument.distribution.DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + return super.newFunctionTimer(id, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit); + } + @Override protected TimeUnit getBaseTimeUnit() { return TimeUnit.MILLISECONDS; @@ -225,8 +260,21 @@ String writeCounter(Counter counter) { // VisibleForTesting String writeFunctionTimer(FunctionTimer timer) { - return writeEvent(timer, event("count", timer.count()), event("sum", timer.totalTime(getBaseTimeUnit())), - event("avg", timer.mean(getBaseTimeUnit()))); + Boolean publishAvg = publishAvgFlags.getOrDefault(timer.getId(), + io.micrometer.core.instrument.distribution.DistributionStatisticConfig.DEFAULT + .isPublishingAverage()); + + Attribute avgAttr = publishAvg ? event("avg", timer.mean(getBaseTimeUnit())) : null; + + if (avgAttr != null) { + return writeEvent(timer, event("count", timer.count()), + event("sum", timer.totalTime(getBaseTimeUnit())), avgAttr); + } + else { + return writeEvent(timer, event("count", timer.count()), + event("sum", timer.totalTime(getBaseTimeUnit()))); + } + } // VisibleForTesting @@ -238,15 +286,42 @@ String writeLongTaskTimer(LongTaskTimer timer) { // VisibleForTesting String writeTimer(Timer timer) { HistogramSnapshot snap = timer.takeSnapshot(); - return writeEvent(timer, event("count", (double) snap.count()), event("sum", snap.total(getBaseTimeUnit())), - event("avg", snap.mean(getBaseTimeUnit())), event("max", snap.max(getBaseTimeUnit()))); + + Boolean publishAvg = publishAvgFlags.getOrDefault(timer.getId(), + DistributionStatisticConfig.DEFAULT.isPublishingAverage()); + + Attribute avgAttr = publishAvg ? event("avg", snap.mean(getBaseTimeUnit())) : null; + + if (avgAttr != null) { + return writeEvent(timer, event("count", (double) snap.count()), + event("sum", snap.total(getBaseTimeUnit())), avgAttr, + event("max", snap.max(getBaseTimeUnit()))); + } + else { + return writeEvent(timer, event("count", (double) snap.count()), + event("sum", snap.total(getBaseTimeUnit())), event("max", snap.max(getBaseTimeUnit()))); + } + } // VisibleForTesting String writeSummary(DistributionSummary summary) { HistogramSnapshot snap = summary.takeSnapshot(); - return writeEvent(summary, event("count", (double) snap.count()), event("sum", snap.total()), - event("avg", snap.mean()), event("max", snap.max())); + Boolean publishAvg = publishAvgFlags.getOrDefault(summary.getId(), + io.micrometer.core.instrument.distribution.DistributionStatisticConfig.DEFAULT + .isPublishingAverage()); + + Attribute avgAttr = publishAvg ? event("avg", snap.mean()) : null; + + if (avgAttr != null) { + return writeEvent(summary, event("count", (double) snap.count()), event("sum", snap.total()), avgAttr, + event("max", snap.max())); + } + else { + return writeEvent(summary, event("count", (double) snap.count()), event("sum", snap.total()), + event("max", snap.max())); + } + } // VisibleForTesting diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/DistributionStatisticConfig.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/DistributionStatisticConfig.java index 22684d954e..acd8fef943 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/DistributionStatisticConfig.java +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/DistributionStatisticConfig.java @@ -43,6 +43,7 @@ public class DistributionStatisticConfig implements Mergeable 0); } + public boolean isPublishingAverage() { + if (publishAverage == null) { + return DEFAULT.publishAverage == null || DEFAULT.publishAverage; + } + return publishAverage; + } + }