From e9cf289787904cd50c0f0bd397fafd7d9abc822d Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Mon, 25 Sep 2023 23:47:34 -0500 Subject: [PATCH] provide a fully constructed metric type which can be registered as-is --- .../api/activityimpl/input/AtomicInput.java | 17 +++-- .../uniform/StandardActivity.java | 18 +++-- .../api/engine/metrics/ActivityMetrics.java | 73 +++++++++++++++---- .../metrics/instruments/NBFunctionGauge.java | 8 +- .../metrics/instruments/NBLabeledMetric.java | 23 ++++++ .../metrics/instruments/NBMetricCounter.java | 3 +- .../metrics/instruments/NBMetricGauge.java | 3 +- .../instruments/NBMetricHistogram.java | 5 +- .../metrics/instruments/NBMetricMeter.java | 3 +- .../metrics/instruments/NBMetricTimer.java | 5 +- 10 files changed, 113 insertions(+), 45 deletions(-) create mode 100644 nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBLabeledMetric.java diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java index f54c27f34..e64323562 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/input/AtomicInput.java @@ -63,14 +63,15 @@ public class AtomicInput implements Input, ActivityDefObserver, Gauge, NBL this.parent = parent; this.activityDef = activityDef; onActivityDefUpdate(activityDef); - ActivityMetrics.gauge(this, "input_cycles_first", new NBFunctionGauge(this, () -> (double)this.cycles_min.get())); - ActivityMetrics.gauge(this, "input_cycles_last", new NBFunctionGauge(this, () -> (double)this.cycles_max.get())); - ActivityMetrics.gauge(this, "input_cycle", new NBFunctionGauge(this, () -> (double)this.cycle_value.get())); - ActivityMetrics.gauge(this, "input_cycles_total", new NBFunctionGauge(this, this::getTotalCycles)); - ActivityMetrics.gauge(this, "input_recycles_first", new NBFunctionGauge(this, () -> (double)this.recycles_min.get())); - ActivityMetrics.gauge(this, "input_recycles_last", new NBFunctionGauge(this, () -> (double)this.recycles_max.get())); - ActivityMetrics.gauge(this, "input_recycle", new NBFunctionGauge(this, () -> (double) this.recycle_value.get())); - ActivityMetrics.gauge(this, "input_recycles_total", new NBFunctionGauge(this, this::getTotalRecycles)); + + ActivityMetrics.register(new NBFunctionGauge(this, () -> (double)this.cycles_min.get(),"input_cycles_first")); + ActivityMetrics.register(new NBFunctionGauge(this, () -> (double)this.cycles_max.get(), "input_cycles_last")); + ActivityMetrics.register(new NBFunctionGauge(this, () -> (double)this.cycle_value.get(), "input_cycle")); + ActivityMetrics.register(new NBFunctionGauge(this, this::getTotalCycles, "input_cycles_total")); + ActivityMetrics.register(new NBFunctionGauge(this, () -> (double)this.recycles_min.get(), "input_recycles_first")); + ActivityMetrics.register(new NBFunctionGauge(this, () -> (double)this.recycles_max.get(), "input_recycles_last")); + ActivityMetrics.register(new NBFunctionGauge(this, () -> (double) this.recycle_value.get(),"input_recycle")); + ActivityMetrics.register(new NBFunctionGauge(this, this::getTotalRecycles,"input_recycles_total")); } private double getTotalRecycles() { diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java index 1cf1c9ec9..6a3a07dc6 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/StandardActivity.java @@ -98,7 +98,7 @@ public class StandardActivity extends SimpleActivity implements for (OpTemplate ot : opTemplates) { ParsedOp incompleteOpDef = new ParsedOp(ot, NBConfiguration.empty(), List.of(), this); String driverName = incompleteOpDef.takeOptionalStaticValue("driver", String.class) - .or(() -> incompleteOpDef.takeOptionalStaticValue("type",String.class)) + .or(() -> incompleteOpDef.takeOptionalStaticValue("type", String.class)) .or(() -> defaultDriverOption) .orElseThrow(() -> new OpConfigError("Unable to identify driver name for op template:\n" + ot)); @@ -152,12 +152,14 @@ public class StandardActivity extends SimpleActivity implements throw new OpConfigError("Error mapping workload template to operations: " + e.getMessage(), null, e); } - this.pendingOpsGauge= ActivityMetrics.gauge(this,"ops_pending", - new NBFunctionGauge(this,() -> this.getProgressMeter().getSummary().pending())); - this.activeOpsGauge = ActivityMetrics.gauge(this,"ops_active", - new NBFunctionGauge(this,() -> this.getProgressMeter().getSummary().current())); - this.completeOpsGauge= ActivityMetrics.gauge(this,"ops_complete", - new NBFunctionGauge(this,() -> this.getProgressMeter().getSummary().complete())); + this.pendingOpsGauge = ActivityMetrics.register( + new NBFunctionGauge(this, () -> this.getProgressMeter().getSummary().pending(), "ops_pending") + ); + this.activeOpsGauge = ActivityMetrics.register( + new NBFunctionGauge(this, () -> this.getProgressMeter().getSummary().current(),"ops_active") + ); + this.completeOpsGauge = ActivityMetrics.register( + new NBFunctionGauge(this, () -> this.getProgressMeter().getSummary().complete(),"ops_complete")); } @Override @@ -217,7 +219,7 @@ public class StandardActivity extends SimpleActivity implements public void shutdownActivity() { for (Map.Entry entry : adapters.entrySet()) { String adapterName = entry.getKey(); - DriverAdapter adapter = entry.getValue(); + DriverAdapter adapter = entry.getValue(); adapter.getSpaceCache().getElements().forEach((spaceName, space) -> { if (space instanceof AutoCloseable autocloseable) { try { diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/ActivityMetrics.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/ActivityMetrics.java index 49877615d..e917af320 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/ActivityMetrics.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/ActivityMetrics.java @@ -76,10 +76,10 @@ public class ActivityMetrics { @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") private static Metric register(NBLabels labels, MetricProvider metricProvider) { - labels = labelFilter!=null ? labelFilter.apply(labels) : labels; + labels = labelFilter != null ? labelFilter.apply(labels) : labels; labels = labelValidator != null ? labelValidator.apply(labels) : labels; - final String graphiteName = labels.linearizeValues('.',"[activity]","[space]","[op]","name"); + final String graphiteName = labels.linearizeValues('.', "[activity]", "[space]", "[op]", "name"); Metric metric = get().getMetrics().get(graphiteName); if (null == metric) { @@ -96,6 +96,45 @@ public class ActivityMetrics { return metric; } + /** + * Calls to this version of register must be done with a pre-built metric instrument. + * This means that it is not suitable for lazily creating metric objects directly on + * instances which are one of many. Instead, call this to register metrics at the start + * of an owning element. + * + * This version of register expects that you have fully labeled a metric, including + * addint the 'name' field, also known as the metric family name in some specifications. + * + * It is due to be replaced by a different registry format soon. + * + * @param labeledMetric + * @return the metric instance + */ + public static M register(M labeledMetric) { + NBLabels labels = labeledMetric.getLabels(); + labels = labelFilter != null ? labelFilter.apply(labels) : labels; + labels = labelValidator != null ? labelValidator.apply(labels) : labels; + + final String graphiteName = labels.linearizeValues('.', "[activity]", "[space]", "[op]", "name"); + Metric metric = get().getMetrics().get(graphiteName); + + metric = get().getMetrics().get(graphiteName); + if (metric!=null) { + logger.warn("Metric already registered for '" + graphiteName + "', probably logic error which could invalidate metric values."); + } else { + get().register(graphiteName, labeledMetric); + } + return labeledMetric; + } + + public static void unregister(NBLabeledElement element) { + final String graphiteName = element.getLabels().linearizeValues('.', "[activity]", "[space]", "[op]", "name"); + if (!get().getMetrics().containsKey(graphiteName)) { + logger.warn("Removing non-extant metric by name: '"+ graphiteName + "'"); + } + get().remove(graphiteName); + } + /** *

Create a timer associated with an activity.

* @@ -112,7 +151,7 @@ public class ActivityMetrics { * @return the timer, perhaps a different one if it has already been registered */ public static Timer timer(NBLabeledElement parent, String metricFamilyName, int hdrdigits) { - final NBLabels labels = parent.getLabels().and("name",sanitize(metricFamilyName)); + final NBLabels labels = parent.getLabels().and("name", sanitize(metricFamilyName)); Timer registeredTimer = (Timer) register(labels, () -> @@ -164,7 +203,7 @@ public class ActivityMetrics { * @return the counter, perhaps a different one if it has already been registered */ public static Counter counter(NBLabeledElement parent, String metricFamilyName) { - final NBLabels labels = parent.getLabels().and("name",metricFamilyName); + final NBLabels labels = parent.getLabels().and("name", metricFamilyName); return (Counter) register(labels, () -> new NBMetricCounter(labels)); } @@ -180,7 +219,7 @@ public class ActivityMetrics { * @return the meter, perhaps a different one if it has already been registered */ public static Meter meter(NBLabeledElement parent, String metricFamilyName) { - final NBLabels labels = parent.getLabels().and("name",sanitize(metricFamilyName)); + final NBLabels labels = parent.getLabels().and("name", sanitize(metricFamilyName)); return (Meter) register(labels, () -> new NBMetricMeter(labels)); } @@ -201,27 +240,30 @@ public class ActivityMetrics { * and so on. It uses the same data reservoir for all views, but only returns one of them as a handle to the metric. * This has the effect of leaving some of the metric objects unreferencable from the caller side. This may need * to be changed in a future update in the even that full inventory management is required on metric objects here. - * @param parent The labeled element the metric pertains to - * @param metricFamilyName The name of the measurement + * + * @param parent + * The labeled element the metric pertains to + * @param metricFamilyName + * The name of the measurement * @return One of the created metrics, suitable for calling {@link DoubleSummaryGauge#accept(double)} on. */ public static DoubleSummaryGauge summaryGauge(NBLabeledElement parent, String metricFamilyName) { DoubleSummaryStatistics stats = new DoubleSummaryStatistics(); DoubleSummaryGauge anyGauge = null; - for (DoubleSummaryGauge.Stat statName: DoubleSummaryGauge.Stat.values()){ + for (DoubleSummaryGauge.Stat statName : DoubleSummaryGauge.Stat.values()) { final NBLabels labels = parent.getLabels() - .and("name",sanitize(metricFamilyName)) - .modifyValue("name", n -> n+"_"+statName.name().toLowerCase()); - anyGauge= (DoubleSummaryGauge) register(labels, () -> new DoubleSummaryGauge(labels,statName,stats)); + .and("name", sanitize(metricFamilyName)) + .modifyValue("name", n -> n + "_" + statName.name().toLowerCase()); + anyGauge = (DoubleSummaryGauge) register(labels, () -> new DoubleSummaryGauge(labels, statName, stats)); } return anyGauge; } + @SuppressWarnings("unchecked") public static Gauge gauge(NBLabeledElement parent, String metricFamilyName, Gauge gauge) { - final NBLabels labels = parent.getLabels().and("name",sanitize(metricFamilyName)); - - return (Gauge) register(labels, () -> new NBMetricGaugeWrapper<>(labels,gauge)); + final NBLabels labels = parent.getLabels().and("name", sanitize(metricFamilyName)); + return (Gauge) register(labels, () -> new NBMetricGaugeWrapper<>(labels, gauge)); } private static MetricRegistry lookupRegistry() { @@ -375,10 +417,11 @@ public class ActivityMetrics { .forEach(get()::remove); } + public static String sanitize(String word) { String sanitized = word; sanitized = sanitized.replaceAll("\\..+$", ""); - sanitized = sanitized.replaceAll("-","_"); + sanitized = sanitized.replaceAll("-", "_"); sanitized = sanitized.replaceAll("[^a-zA-Z0-9_]+", ""); if (!word.equals(sanitized)) { diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBFunctionGauge.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBFunctionGauge.java index 72841e246..5f5913e68 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBFunctionGauge.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBFunctionGauge.java @@ -19,6 +19,7 @@ package io.nosqlbench.api.engine.metrics.instruments; import io.nosqlbench.api.labels.NBLabeledElement; import io.nosqlbench.api.labels.NBLabels; +import java.util.Map; import java.util.function.Supplier; public class NBFunctionGauge implements NBMetricGauge { @@ -27,11 +28,14 @@ public class NBFunctionGauge implements NBMetricGauge { private final NBLabeledElement parent; private final NBLabels labels; - public NBFunctionGauge(NBLabeledElement parent, Supplier source, Object... labels) { + public NBFunctionGauge(NBLabeledElement parent, Supplier source, String metricFamilyName, Map additionalLabels) { this.parent = parent; - this.labels = NBLabels.forKV(labels); + this.labels = NBLabels.forMap(additionalLabels).and("name",metricFamilyName); this.source = source; } + public NBFunctionGauge(NBLabeledElement parent, Supplier source, String metricFamilyName) { + this(parent, source, metricFamilyName,Map.of()); + } @Override public Double getValue() { return source.get(); diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBLabeledMetric.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBLabeledMetric.java new file mode 100644 index 000000000..62c0caeb0 --- /dev/null +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBLabeledMetric.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 nosqlbench + * + * 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. + */ + +package io.nosqlbench.api.engine.metrics.instruments; + +import com.codahale.metrics.Metric; +import io.nosqlbench.api.labels.NBLabeledElement; + +public interface NBLabeledMetric extends Metric, NBLabeledElement { +} diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricCounter.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricCounter.java index 809e2207e..5e4013702 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricCounter.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricCounter.java @@ -17,10 +17,9 @@ package io.nosqlbench.api.engine.metrics.instruments; import com.codahale.metrics.Counter; -import io.nosqlbench.api.labels.NBLabeledElement; import io.nosqlbench.api.labels.NBLabels; -public class NBMetricCounter extends Counter implements NBLabeledElement { +public class NBMetricCounter extends Counter implements NBLabeledMetric { private final NBLabels labels; diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricGauge.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricGauge.java index 1d3ebd51c..e9e018e0c 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricGauge.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricGauge.java @@ -17,8 +17,7 @@ package io.nosqlbench.api.engine.metrics.instruments; import com.codahale.metrics.Gauge; -import io.nosqlbench.api.labels.NBLabeledElement; -public interface NBMetricGauge extends Gauge, NBLabeledElement { +public interface NBMetricGauge extends Gauge, NBLabeledMetric { } diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricHistogram.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricHistogram.java index 2247a3c79..f495de6d9 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricHistogram.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricHistogram.java @@ -17,15 +17,14 @@ package io.nosqlbench.api.engine.metrics.instruments; import com.codahale.metrics.Histogram; -import io.nosqlbench.api.labels.NBLabeledElement; -import io.nosqlbench.api.labels.NBLabels; import io.nosqlbench.api.engine.metrics.*; +import io.nosqlbench.api.labels.NBLabels; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -public class NBMetricHistogram extends Histogram implements DeltaSnapshotter, HdrDeltaHistogramAttachment, HistogramAttachment, NBLabeledElement { +public class NBMetricHistogram extends Histogram implements DeltaSnapshotter, HdrDeltaHistogramAttachment, HistogramAttachment, NBLabeledMetric { private final DeltaHdrHistogramReservoir hdrDeltaReservoir; private final NBLabels labels; diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricMeter.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricMeter.java index a374a889a..3a3afb47c 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricMeter.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricMeter.java @@ -17,10 +17,9 @@ package io.nosqlbench.api.engine.metrics.instruments; import com.codahale.metrics.Meter; -import io.nosqlbench.api.labels.NBLabeledElement; import io.nosqlbench.api.labels.NBLabels; -public class NBMetricMeter extends Meter implements NBLabeledElement { +public class NBMetricMeter extends Meter implements NBLabeledMetric { private final NBLabels labels; diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricTimer.java b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricTimer.java index 0802c0eb8..bbcd16fe2 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricTimer.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/metrics/instruments/NBMetricTimer.java @@ -17,16 +17,15 @@ package io.nosqlbench.api.engine.metrics.instruments; import com.codahale.metrics.Timer; -import io.nosqlbench.api.labels.NBLabeledElement; -import io.nosqlbench.api.labels.NBLabels; import io.nosqlbench.api.engine.metrics.*; +import io.nosqlbench.api.labels.NBLabels; import org.HdrHistogram.Histogram; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; -public class NBMetricTimer extends Timer implements DeltaSnapshotter, HdrDeltaHistogramAttachment, TimerAttachment, NBLabeledElement { +public class NBMetricTimer extends Timer implements DeltaSnapshotter, HdrDeltaHistogramAttachment, TimerAttachment, NBLabeledMetric { private final DeltaHdrHistogramReservoir deltaHdrHistogramReservoir; private long cacheExpiry; private List mirrors;