From 8fb7db1835dc31bc263cbdcbf9b363753159fb6a Mon Sep 17 00:00:00 2001 From: Juha Mynttinen Date: Thu, 10 Jul 2025 12:20:10 +0300 Subject: [PATCH 1/5] inkless azure storage MetricRegistry.all() static --- .../io/aiven/inkless/storage_backend/azure/MetricRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/azure/MetricRegistry.java b/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/azure/MetricRegistry.java index 5dba2136d4..91bcd23edc 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/azure/MetricRegistry.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/azure/MetricRegistry.java @@ -104,7 +104,7 @@ public class MetricRegistry { TOTAL_DOC_PREFIX + BLOB_GET_DOC ); - public List all() { + public static List all() { return List.of( BLOB_DELETE_RATE_METRIC_NAME, BLOB_DELETE_TOTAL_METRIC_NAME, From 03de9ec87c3a961f6c9b37c0abf44460ef84f6d0 Mon Sep 17 00:00:00 2001 From: Juha Mynttinen Date: Thu, 10 Jul 2025 11:15:15 +0300 Subject: [PATCH 2/5] inkless gcs storage MetricRegistry.all() static --- .../io/aiven/inkless/storage_backend/gcs/MetricRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/gcs/MetricRegistry.java b/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/gcs/MetricRegistry.java index 99cdbb72cd..6290ad3bc1 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/gcs/MetricRegistry.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/gcs/MetricRegistry.java @@ -104,7 +104,7 @@ public class MetricRegistry { TOTAL_DOC_PREFIX + RESUMABLE_CHUNK_UPLOAD_DOC ); - public List all() { + public static List all() { return List.of( OBJECT_METADATA_GET_RATE_METRIC_NAME, OBJECT_METADATA_GET_TOTAL_METRIC_NAME, From 3232b71c943eedce43250a96996606edb4b8326d Mon Sep 17 00:00:00 2001 From: Juha Mynttinen Date: Thu, 10 Jul 2025 12:24:52 +0300 Subject: [PATCH 3/5] inkless S3 storage MetricRegistry.all() static --- .../io/aiven/inkless/storage_backend/s3/MetricRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/s3/MetricRegistry.java b/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/s3/MetricRegistry.java index 6408d6fca4..61ffd07d7e 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/s3/MetricRegistry.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/storage_backend/s3/MetricRegistry.java @@ -332,7 +332,7 @@ public class MetricRegistry { TOTAL_DOC_PREFIX + OTHER_ERRORS_DOC ); - public List all() { + public static List all() { return List.of( GET_OBJECT_REQUESTS_RATE_METRIC_NAME, GET_OBJECT_REQUESTS_TOTAL_METRIC_NAME, From d43b85321ecfdc16234b6b0079d6eb36cc9d0651 Mon Sep 17 00:00:00 2001 From: Juha Mynttinen Date: Wed, 9 Jul 2025 08:36:49 +0300 Subject: [PATCH 4/5] Create docs/inkless/metrics.rst Add Gradle task :storage:inkless:genInklessMetricsDoc that generates the file docs/inkless/metrics.rst documenting Inkless-specific metrics. The task executes aiven.inkless.doc.MetricsDocs. At this point only AWS S3 storage backend metrics, Azure storage backend metrics and Google Cloud Storage backend metrics are included. --- build.gradle | 14 ++ docs/inkless/README.md | 2 + docs/inkless/metrics.rst | 103 ++++++++++++ .../io/aiven/inkless/doc/MetricsDocs.java | 150 ++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 docs/inkless/metrics.rst create mode 100644 storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java diff --git a/build.gradle b/build.gradle index d9cdba5454..bd2a06e71a 100644 --- a/build.gradle +++ b/build.gradle @@ -2494,6 +2494,19 @@ project(':storage:inkless') { } } + task genInklessMetricsDoc(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + mainClass = 'io.aiven.inkless.doc.MetricsDocs' + + // Define the outputs formally + outputs.file("$rootDir/docs/inkless/metrics.rst") + + // Set up the output in the execution phase, not configuration, and avoid removing on clean + doFirst { + standardOutput = new File("$rootDir/docs/inkless/metrics.rst").newOutputStream() + } + } + tasks { generateJooqClasses { withContainer { @@ -2603,6 +2616,7 @@ project(':storage:inkless') { tasks.named('build') { dependsOn tasks.named('genInklessConfigDoc') dependsOn tasks.named('genInklessTopicConfigDoc') + dependsOn tasks.named('genInklessMetricsDoc') } } diff --git a/docs/inkless/README.md b/docs/inkless/README.md index 311dd9c503..7c9797d97a 100644 --- a/docs/inkless/README.md +++ b/docs/inkless/README.md @@ -14,4 +14,6 @@ For configuration references, see [Configs](configs.rst) For a Topic Config documentation, see [Topic Configs](topic_configs.rst) +For metrics documentation, see [Metrics](metrics.rst) + Inkless is not supposed to be a long-term fork. We don't accept patches. The code is open exclusively for information purposes. We actively work on contributing these changes to Apache Kafka, see [KIP-1150: Diskless Topics](https://cwiki.apache.org/confluence/display/KAFKA/KIP-1150%3A+Diskless+Topics). diff --git a/docs/inkless/metrics.rst b/docs/inkless/metrics.rst new file mode 100644 index 0000000000..27685472d1 --- /dev/null +++ b/docs/inkless/metrics.rst @@ -0,0 +1,103 @@ +================= +Inkless metrics +================= +.. Generated by io.aiven.inkless.doc.MetricsDocs + +S3Storage metrics +================================== + +aiven.inkless.server.s3:type=s3-client-metrics +---------------------------------------------- + +========================================= ============================================================================= +Attribute name Description +========================================= ============================================================================= +abort-multipart-upload-requests-rate Rate of abort multi-part upload operations +abort-multipart-upload-requests-total Total number of abort multi-part upload operations +abort-multipart-upload-time-avg Average time spent aborting a new multi-part upload operation +abort-multipart-upload-time-max Maximum time spent aborting a new multi-part upload operation +complete-multipart-upload-requests-rate Rate of complete multi-part upload operations +complete-multipart-upload-requests-total Total number of complete multi-part upload operations +complete-multipart-upload-time-avg Average time spent completing a new multi-part upload operation +complete-multipart-upload-time-max Maximum time spent completing a new multi-part upload operation +configured-timeout-errors-rate Rate of configured timeout errors +configured-timeout-errors-total Total number of configured timeout errors +create-multipart-upload-requests-rate Rate of create multi-part upload operations +create-multipart-upload-requests-total Total number of create multi-part upload operations +create-multipart-upload-time-avg Average time spent creating a new multi-part upload operation +create-multipart-upload-time-max Maximum time spent creating a new multi-part upload operation +delete-object-requests-rate Rate of delete object request operations +delete-object-requests-total Total number of delete object request operations +delete-object-time-avg Average time spent deleting an object +delete-object-time-max Maximum time spent deleting an object +delete-objects-requests-rate Rate of delete a set of objects request operations +delete-objects-requests-total Total number of delete a set of objects request operations +delete-objects-time-avg Average time spent deleting a set of objects +delete-objects-time-max Maximum time spent deleting a set of objects +get-object-requests-rate Rate of get object request operations +get-object-requests-total Total number of get object request operations +get-object-time-avg Average time spent getting a response from a get object request +get-object-time-max Maximum time spent getting a response from a get object request +io-errors-rate Rate of IO errors +io-errors-total Total number of IO errors +other-errors-rate Rate of other errors +other-errors-total Total number of other errors +put-object-requests-rate Rate of put object request operations +put-object-requests-total Total number of put object request operations +put-object-time-avg Average time spent uploading an object +put-object-time-max Maximum time spent uploading an object +server-errors-rate Rate of server errors +server-errors-total Total number of server errors +throttling-errors-rate Rate of throttling errors +throttling-errors-total Total number of throttling errors +upload-part-requests-rate Rate of upload part request operations (as part of multi-part upload) +upload-part-requests-total Total number of upload part request operations (as part of multi-part upload) +upload-part-time-avg Average time spent uploading a single part +upload-part-time-max Maximum time spent uploading a single part +========================================= ============================================================================= + + +AzureBlobStorage metrics +================================== + +io.aiven.inkless.storage.azure:type=azure-blob-storage-client-metrics +--------------------------------------------------------------------- + +======================== ============================================================ +Attribute name Description +======================== ============================================================ +blob-delete-rate Rate of object delete operations +blob-delete-total Total number of object delete operations +blob-get-rate Rate of get object operations +blob-get-total Total number of get object operations +blob-upload-rate Rate of object upload operations +blob-upload-total Total number of object upload operations +block-list-upload-rate Rate of block list (making a blob) upload operations +block-list-upload-total Total number of block list (making a blob) upload operations +block-upload-rate Rate of block (blob part) upload operations +block-upload-total Total number of block (blob part) upload operations +======================== ============================================================ + + +GcsStorage metrics +================================== + +io.aiven.inkless.storage.gcs:type=gcs-client-metrics +---------------------------------------------------- + +================================ =================================================================== +Attribute name Description +================================ =================================================================== +object-delete-rate Rate of delete object operations +object-delete-total Total number of delete object operations +object-get-rate Rate of get object operations +object-get-total Total number of get object operations +object-metadata-get-rate Rate of get object metadata operations +object-metadata-get-total Total number of get object metadata operations +resumable-chunk-upload-rate Rate of upload chunk operations as part of resumable upload +resumable-chunk-upload-total Total number of upload chunk operations as part of resumable upload +resumable-upload-initiate-rate Rate of initiate resumable upload operations +resumable-upload-initiate-total Total number of initiate resumable upload operations +================================ =================================================================== + + diff --git a/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java b/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java new file mode 100644 index 0000000000..7e775127c3 --- /dev/null +++ b/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java @@ -0,0 +1,150 @@ +/* + * Inkless + * Copyright (C) 2024 - 2025 Aiven OY + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package io.aiven.inkless.doc; + +import org.apache.kafka.common.MetricName; +import org.apache.kafka.common.MetricNameTemplate; +import org.apache.kafka.common.metrics.Metrics; +import org.apache.kafka.common.utils.Sanitizer; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import static java.lang.System.out; + +public class MetricsDocs { + public static void main(final String[] args) { + printDocumentHeading("Inkless metrics"); + + out.println(".. Generated by " + MetricsDocs.class.getCanonicalName()); + out.println(); + + // AWS S3 storage backend metrics + printHeading(io.aiven.inkless.storage_backend.s3.S3Storage.class.getSimpleName() + " metrics"); + out.println(); + out.println(toRstTable(io.aiven.inkless.storage_backend.s3.MetricRegistry.METRIC_CONTEXT, io.aiven.inkless.storage_backend.s3.MetricRegistry.all())); + + // Azure storage backend metrics + printHeading(io.aiven.inkless.storage_backend.azure.AzureBlobStorage.class.getSimpleName() + " metrics"); + out.println(); + out.println(toRstTable(io.aiven.inkless.storage_backend.azure.MetricRegistry.METRIC_CONTEXT, io.aiven.inkless.storage_backend.azure.MetricRegistry.all())); + + // Google Cloud Storage backend metrics + printHeading(io.aiven.inkless.storage_backend.gcs.GcsStorage.class.getSimpleName() + " metrics"); + out.println(); + out.println(toRstTable(io.aiven.inkless.storage_backend.gcs.MetricRegistry.METRIC_CONTEXT, io.aiven.inkless.storage_backend.gcs.MetricRegistry.all())); + } + + // o.a.k.common.metrics.Metrics does only have generation of Html documentation. + // as there is no plans to publish HTML docs, this util method is added to generate RST. + // may be upstreamed. + static String toRstTable(final String domain, final Iterable allMetrics) { + final Map> beansAndAttributes = new TreeMap<>(); + + try (final Metrics metrics = new Metrics()) { + for (final MetricNameTemplate template : allMetrics) { + final Map tags = new LinkedHashMap<>(); + for (final String s : template.tags()) { + tags.put(s, "{" + s + "}"); + } + + final MetricName metricName = metrics.metricName( + template.name(), + template.group(), + template.description(), + tags + ); + final String beanName = getMBeanName(domain, metricName); + beansAndAttributes.computeIfAbsent(beanName, k -> new TreeMap<>()); + final Map attrAndDesc = beansAndAttributes.get(beanName); + if (!attrAndDesc.containsKey(template.name())) { + attrAndDesc.put(template.name(), template.description()); + } else { + throw new IllegalArgumentException( + "mBean '" + beanName + + "' attribute '" + + template.name() + + "' is defined twice." + ); + } + } + } + + final StringBuilder b = new StringBuilder(); + + for (final Map.Entry> e : beansAndAttributes.entrySet()) { + // Add mBean name as a section title + b.append(e.getKey()).append("\n"); + b.append("-".repeat(e.getKey().length())).append("\n\n"); + + // Determine the maximum lengths for each column + final int maxAttrLength = Math.max("Attribute name".length(), + e.getValue().keySet().stream().mapToInt(String::length).max().orElse(0)); + final int maxDescLength = Math.max("Description".length(), + e.getValue().values().stream().mapToInt(String::length).max().orElse(0)); + + // Create the table header + final String headerFormat = "%-" + maxAttrLength + "s %-" + maxDescLength + "s\n"; + final String separatorLine = "=" + "=".repeat(maxAttrLength) + " " + "=".repeat(maxDescLength) + "\n"; + + b.append(separatorLine); + b.append(String.format(headerFormat, "Attribute name", "Description")); + b.append(separatorLine); + + // Add table rows + for (final Map.Entry e2 : e.getValue().entrySet()) { + b.append(String.format(headerFormat, e2.getKey(), e2.getValue())); + } + + // Close the table + b.append(separatorLine); + b.append("\n"); // Add an empty line between tables + } + + return b.toString(); + } + + static void printDocumentHeading(final String title) { + out.println("=================\n" + title + "\n" + "================="); + } + + static void printHeading(final String title) { + out.println(title + "\n" + "=================================="); + } + + /** + * {@link org.apache.kafka.common.metrics.JmxReporter#getMBeanName(String, MetricName)} + */ + static String getMBeanName(String prefix, MetricName metricName) { + StringBuilder mBeanName = new StringBuilder(); + mBeanName.append(prefix); + mBeanName.append(":type="); + mBeanName.append(metricName.group()); + for (Map.Entry entry : metricName.tags().entrySet()) { + if (entry.getKey().isEmpty() || entry.getValue().isEmpty()) + continue; + mBeanName.append(","); + mBeanName.append(entry.getKey()); + mBeanName.append("="); + mBeanName.append(Sanitizer.jmxSanitize(entry.getValue())); + } + return mBeanName.toString(); + } +} \ No newline at end of file From f0d30e7d0c8c0c250caab6e4ab38a92be6efc7b1 Mon Sep 17 00:00:00 2001 From: Juha Mynttinen Date: Fri, 11 Jul 2025 08:51:01 +0300 Subject: [PATCH 5/5] Inkless Writer metrics in metrics.rst In order to expose the metrics, refactor WriterMetrics. For each metric introduce a corresponding MetricName. Add the description constants for all metrics. Finally, expose the metrics as MetricNameTemplate. --- docs/inkless/metrics.rst | 15 ++++++ .../server/metrics/KafkaMetricsGroup.java | 5 ++ .../io/aiven/inkless/doc/MetricsDocs.java | 11 +++- .../java/io/aiven/inkless/produce/Writer.java | 2 +- .../aiven/inkless/produce/WriterMetrics.java | 54 ++++++++++++++++--- 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/docs/inkless/metrics.rst b/docs/inkless/metrics.rst index 27685472d1..5e3e52c9ff 100644 --- a/docs/inkless/metrics.rst +++ b/docs/inkless/metrics.rst @@ -3,6 +3,21 @@ Inkless metrics ================= .. Generated by io.aiven.inkless.doc.MetricsDocs +Writer metrics +================================== + +io.aiven.inkless.produce:type=io.aiven.inkless.produce +------------------------------------------------------ + +=============== ================================================= +Attribute name Description +=============== ================================================= +RequestRate The number of write requests per unit time. +RotationRate The number of file rotations per unit time. +RotationTime The time taken to rotate a file, in milliseconds. +=============== ================================================= + + S3Storage metrics ================================== diff --git a/server-common/src/main/java/org/apache/kafka/server/metrics/KafkaMetricsGroup.java b/server-common/src/main/java/org/apache/kafka/server/metrics/KafkaMetricsGroup.java index 5a83c0d94a..9825445a72 100644 --- a/server-common/src/main/java/org/apache/kafka/server/metrics/KafkaMetricsGroup.java +++ b/server-common/src/main/java/org/apache/kafka/server/metrics/KafkaMetricsGroup.java @@ -114,6 +114,10 @@ public final Histogram newHistogram(String name, boolean biased, Map tags) { + return KafkaYammerMetrics.defaultRegistry().newHistogram(metricName, biased); + } + public final Histogram newHistogram(String name) { return newHistogram(name, true, Map.of()); } @@ -172,3 +176,4 @@ private static Optional toScope(Map tags) { } } } + diff --git a/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java b/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java index 7e775127c3..7184ee2764 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/doc/MetricsDocs.java @@ -18,11 +18,14 @@ package io.aiven.inkless.doc; +import io.aiven.inkless.produce.WriterMetrics; import org.apache.kafka.common.MetricName; import org.apache.kafka.common.MetricNameTemplate; import org.apache.kafka.common.metrics.Metrics; import org.apache.kafka.common.utils.Sanitizer; +import org.apache.kafka.common.utils.Time; +import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; @@ -30,12 +33,18 @@ import static java.lang.System.out; public class MetricsDocs { - public static void main(final String[] args) { + public static void main(final String[] args) throws IOException { printDocumentHeading("Inkless metrics"); out.println(".. Generated by " + MetricsDocs.class.getCanonicalName()); out.println(); + printHeading(io.aiven.inkless.produce.Writer.class.getSimpleName() + " metrics"); + try (WriterMetrics writerMetrics = new WriterMetrics(Time.SYSTEM)) { + out.println(); + out.println(toRstTable(io.aiven.inkless.produce.WriterMetrics.class.getPackageName(), writerMetrics.all())); + } + // AWS S3 storage backend metrics printHeading(io.aiven.inkless.storage_backend.s3.S3Storage.class.getSimpleName() + " metrics"); out.println(); diff --git a/storage/inkless/src/main/java/io/aiven/inkless/produce/Writer.java b/storage/inkless/src/main/java/io/aiven/inkless/produce/Writer.java index 729df88709..1d3d2e7244 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/produce/Writer.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/produce/Writer.java @@ -64,7 +64,7 @@ * *

The class is thread-safe: all the event entry points are protected with the lock.

*/ -class Writer implements Closeable { +public class Writer implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(Writer.class); private final Lock lock = new ReentrantLock(); diff --git a/storage/inkless/src/main/java/io/aiven/inkless/produce/WriterMetrics.java b/storage/inkless/src/main/java/io/aiven/inkless/produce/WriterMetrics.java index 3204defe25..d8d32c5088 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/produce/WriterMetrics.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/produce/WriterMetrics.java @@ -17,6 +17,8 @@ */ package io.aiven.inkless.produce; +import com.yammer.metrics.core.MetricName; +import org.apache.kafka.common.MetricNameTemplate; import org.apache.kafka.common.utils.Time; import org.apache.kafka.server.metrics.KafkaMetricsGroup; @@ -27,6 +29,7 @@ import java.io.IOException; import java.time.Duration; import java.time.Instant; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.LongAdder; @@ -35,21 +38,36 @@ @CoverageIgnore public class WriterMetrics implements Closeable { + public static final KafkaMetricsGroup METRICS_GROUP = new KafkaMetricsGroup(WriterMetrics.class); + public static final String REQUEST_RATE = "RequestRate"; + public static final String REQUEST_RATE_DESCRIPTION = "The number of write requests per unit time."; + public static final String ROTATION_RATE = "RotationRate"; + public static final String ROTATION_RATE_DESCRIPTION = "The number of file rotations per unit time."; + public static final String ROTATION_TIME = "RotationTime"; - private final KafkaMetricsGroup metricsGroup = new KafkaMetricsGroup(WriterMetrics.class); + public static final String ROTATION_TIME_DESCRIPTION = "The time taken to rotate a file, in milliseconds."; + + private final MetricName requestRateMetricName; + private final MetricName rotationRateMetricName; + private final MetricName rotationTimeMetricName; + private final Histogram rotationTime; final Time time; final LongAdder requests = new LongAdder(); final LongAdder rotations = new LongAdder(); + public WriterMetrics(final Time time) { this.time = Objects.requireNonNull(time, "time cannot be null"); - metricsGroup.newGauge(REQUEST_RATE, requests::intValue); - metricsGroup.newGauge(ROTATION_RATE, rotations::intValue); - rotationTime = metricsGroup.newHistogram(ROTATION_TIME, true, Map.of()); + this.requestRateMetricName = METRICS_GROUP.metricName(REQUEST_RATE, Map.of()); + METRICS_GROUP.newGauge(this.requestRateMetricName, requests::intValue); + this.rotationRateMetricName = METRICS_GROUP.metricName(ROTATION_RATE, Map.of()); + METRICS_GROUP.newGauge(this.rotationRateMetricName, rotations::intValue); + this.rotationTimeMetricName = METRICS_GROUP.metricName(ROTATION_TIME, Map.of()); + rotationTime = METRICS_GROUP.newHistogram(this.rotationTimeMetricName, true, Map.of()); } public void requestAdded() { @@ -64,8 +82,28 @@ public void fileRotated(Instant openedAt) { @Override public void close() throws IOException { - metricsGroup.removeMetric(REQUEST_RATE); - metricsGroup.removeMetric(ROTATION_RATE); - metricsGroup.removeMetric(ROTATION_TIME); + METRICS_GROUP.removeMetric(REQUEST_RATE); + METRICS_GROUP.removeMetric(ROTATION_RATE); + METRICS_GROUP.removeMetric(ROTATION_TIME); + } + + public List all() { + return List.of( + new MetricNameTemplate( + requestRateMetricName.getName(), + requestRateMetricName.getGroup(), + REQUEST_RATE_DESCRIPTION + ), + new MetricNameTemplate( + rotationRateMetricName.getName(), + rotationRateMetricName.getGroup(), + ROTATION_RATE_DESCRIPTION + ), + new MetricNameTemplate( + rotationTimeMetricName.getName(), + rotationTimeMetricName.getGroup(), + ROTATION_TIME_DESCRIPTION + ) + ); } -} +} \ No newline at end of file