diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/noop/NoOpMetricsSystem.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/noop/NoOpMetricsSystem.java index 5f876fa4d80..33143c8c75d 100644 --- a/metrics/core/src/main/java/org/hyperledger/besu/metrics/noop/NoOpMetricsSystem.java +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/noop/NoOpMetricsSystem.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.metrics.Observation; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.metrics.Histogram; import org.hyperledger.besu.plugin.services.metrics.LabelledGauge; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; @@ -45,6 +46,9 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem { /** The constant NO_OP_OPERATION_TIMER. */ public static final OperationTimer NO_OP_OPERATION_TIMER = () -> NO_OP_TIMING_CONTEXT; + /** The constant NO_OP_HISTOGRAM. */ + public static final Histogram NO_OP_HISTOGRAM = d -> {}; + /** The constant NO_OP_LABELLED_1_COUNTER. */ public static final LabelledMetric NO_OP_LABELLED_1_COUNTER = new LabelCountingNoOpMetric<>(1, NO_OP_COUNTER); @@ -61,6 +65,10 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem { public static final LabelledMetric NO_OP_LABELLED_1_OPERATION_TIMER = new LabelCountingNoOpMetric<>(1, NO_OP_OPERATION_TIMER); + /** The constant NO_OP_LABELLED_1_HISTOGRAM. */ + public static final LabelledMetric NO_OP_LABELLED_1_HISTOGRAM = + new LabelCountingNoOpMetric<>(1, NO_OP_HISTOGRAM); + /** The constant NO_OP_LABELLED_1_GAUGE. */ public static final LabelledGauge NO_OP_LABELLED_1_GAUGE = new LabelledGaugeNoOpMetric(1, NO_OP_GAUGE); @@ -137,6 +145,20 @@ public static LabelledMetric getOperationTimerLabelledMetric( } } + /** + * Gets histogram labelled metric. + * + * @param labelCount the label count + * @return the histogram labelled metric + */ + public static LabelledMetric getHistogramLabelledMetric(final int labelCount) { + if (labelCount == 1) { + return NO_OP_LABELLED_1_HISTOGRAM; + } else { + return new LabelCountingNoOpMetric<>(labelCount, NO_OP_HISTOGRAM); + } + } + @Override public void createGauge( final MetricCategory category, @@ -144,6 +166,16 @@ public void createGauge( final String help, final DoubleSupplier valueSupplier) {} + @Override + public LabelledMetric createLabelledHistogram( + final MetricCategory category, + final String name, + final String help, + final double[] buckets, + final String... labelNames) { + return null; + } + @Override public LabelledGauge createLabelledGauge( final MetricCategory category, diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java index a399b283734..e12edbc5e53 100644 --- a/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.metrics.StandardMetricCategory; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.metrics.Histogram; import org.hyperledger.besu.plugin.services.metrics.LabelledGauge; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; @@ -277,6 +278,16 @@ public void createGauge( } } + @Override + public LabelledMetric createLabelledHistogram( + final MetricCategory category, + final String name, + final String help, + final double[] buckets, + final String... labelNames) { + return null; + } + @Override public LabelledGauge createLabelledGauge( final MetricCategory category, diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusHistogram.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusHistogram.java new file mode 100644 index 00000000000..825f967a64d --- /dev/null +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusHistogram.java @@ -0,0 +1,45 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.metrics.prometheus; + +import org.hyperledger.besu.plugin.services.metrics.Histogram; +import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; + +class PrometheusHistogram implements LabelledMetric { + + private final io.prometheus.client.Histogram histogram; + + public PrometheusHistogram(final io.prometheus.client.Histogram histogram) { + this.histogram = histogram; + } + + @Override + public Histogram labels(final String... labels) { + return new UnlabelledHistogram(histogram.labels(labels)); + } + + private static class UnlabelledHistogram implements Histogram { + private final io.prometheus.client.Histogram.Child amount; + + private UnlabelledHistogram(final io.prometheus.client.Histogram.Child amount) { + this.amount = amount; + } + + @Override + public void observe(final double amount) { + this.amount.observe(amount); + } + } +} diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusMetricsSystem.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusMetricsSystem.java index 653f448311f..35ad8a2ab74 100644 --- a/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusMetricsSystem.java +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/prometheus/PrometheusMetricsSystem.java @@ -59,6 +59,8 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem { cachedCounters = new ConcurrentHashMap<>(); private final Map> cachedTimers = new ConcurrentHashMap<>(); + private final Map> + cachedHistograms = new ConcurrentHashMap<>(); private final Set totalSuffixedCounters = new ConcurrentHashSet<>(); private final Set enabledCategories; @@ -111,6 +113,29 @@ public LabelledMetric crea }); } + @Override + public LabelledMetric + createLabelledHistogram( + final MetricCategory category, + final String name, + final String help, + final double[] buckets, + final String... labelNames) { + final String metricName = convertToPrometheusName(category, name); + return cachedHistograms.computeIfAbsent( + metricName, + k -> { + if (isCategoryEnabled(category)) { + final Histogram histogram = + Histogram.build(metricName, help).labelNames(labelNames).buckets(buckets).create(); + addCollectorUnchecked(category, histogram); + return new PrometheusHistogram(histogram); + } else { + return NoOpMetricsSystem.getHistogramLabelledMetric(labelNames.length); + } + }); + } + @Override public LabelledMetric createLabelledTimer( final MetricCategory category, diff --git a/metrics/core/src/test-support/java/org/hyperledger/besu/metrics/StubMetricsSystem.java b/metrics/core/src/test-support/java/org/hyperledger/besu/metrics/StubMetricsSystem.java index 2e0ea006db0..6ad2f844968 100644 --- a/metrics/core/src/test-support/java/org/hyperledger/besu/metrics/StubMetricsSystem.java +++ b/metrics/core/src/test-support/java/org/hyperledger/besu/metrics/StubMetricsSystem.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.metrics.Histogram; import org.hyperledger.besu.plugin.services.metrics.LabelledGauge; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; @@ -93,6 +94,16 @@ public void createGauge( gauges.put(name, valueSupplier); } + @Override + public LabelledMetric createLabelledHistogram( + final MetricCategory category, + final String name, + final String help, + final double[] buckets, + final String... labelNames) { + return null; + } + public double getGaugeValue(final String name) { final DoubleSupplier gauge = gauges.get(name); if (gauge == null) { diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index b48dfe96f35..20117d10bb1 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -71,7 +71,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = '8rPIE3fYl48RPRQXxYhMk559e/r+wHSKU9bGSJmruKQ=' + knownHash = '2CKmXhWO+/sZMuA3QpJGvOQVi5t3RA5x72aXoYBIzkU=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java index 80e02a6dba7..30badde8b9b 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/MetricsSystem.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.plugin.services; import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.metrics.Histogram; import org.hyperledger.besu.plugin.services.metrics.LabelledGauge; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; @@ -159,4 +160,17 @@ default void createLongGauge( final LongSupplier valueSupplier) { createGauge(category, name, help, () -> (double) valueSupplier.getAsLong()); } + + /** + * Creates a histogram with assigned labels + * + * @param category The {@link MetricCategory} this histogram is assigned to. + * @param name A name for this metric. + * @param help A human-readable description of the metric. + * @param buckets An array of buckets to assign to the histogram + * @param labelNames An array of labels to assign to the histogram. + * @return The labelled histogram. + */ + LabelledMetric createLabelledHistogram( + MetricCategory category, String name, String help, double[] buckets, String... labelNames); } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/Histogram.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/Histogram.java new file mode 100644 index 00000000000..5f252fdfd1a --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/metrics/Histogram.java @@ -0,0 +1,29 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.plugin.services.metrics; + +/** + * A histogram samples observations (usually things like request durations or response sizes) and + * counts them in configurable buckets. It also provides a sum of all observed values. + */ +public interface Histogram { + + /** + * Observe the given amount. + * + * @param amount the amount + */ + void observe(double amount); +}