This is an automated email from the ASF dual-hosted git repository.
mchades pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 4e802751e6 [#8595] improvement(metrics): add metrics for fileset
catalog (#8602)
4e802751e6 is described below
commit 4e802751e670a715be88c3a0e2173bb0d4ec31d8
Author: mchades <[email protected]>
AuthorDate: Mon Sep 22 18:14:28 2025 +0800
[#8595] improvement(metrics): add metrics for fileset catalog (#8602)
### What changes were proposed in this pull request?
- add a catalog metric name specification:
- add metrics for fileset catalog
### Why are the changes needed?
Fix: #8595
### Does this PR introduce _any_ user-facing change?
no
### How was this patch tested?
test added
---
catalogs/catalog-fileset/build.gradle.kts | 4 +-
.../catalog/fileset/FilesetCatalogOperations.java | 76 ++++++++++----
.../gravitino/metrics/GravitinoSampleBuilder.java | 110 +++++++++++++++++++++
.../org/apache/gravitino/metrics/MetricNames.java | 1 +
.../apache/gravitino/metrics/MetricsSystem.java | 60 ++++++-----
.../CatalogMetricsSource.java} | 19 ++--
.../FilesetCatalogMetricsSource.java} | 13 +--
.../gravitino/metrics/source/MetricsSource.java | 1 +
.../metrics/TestGravitinoSampleBuilder.java | 109 ++++++++++++++++++++
docs/metrics.md | 12 +++
gradle/libs.versions.toml | 1 +
11 files changed, 346 insertions(+), 60 deletions(-)
diff --git a/catalogs/catalog-fileset/build.gradle.kts
b/catalogs/catalog-fileset/build.gradle.kts
index 88382a38a8..c3c41ed530 100644
--- a/catalogs/catalog-fileset/build.gradle.kts
+++ b/catalogs/catalog-fileset/build.gradle.kts
@@ -40,6 +40,7 @@ dependencies {
implementation(project(":core")) {
exclude(group = "*")
}
+ implementation(libs.awaitility)
implementation(libs.caffeine)
implementation(libs.commons.lang3)
implementation(libs.commons.io)
@@ -61,7 +62,8 @@ dependencies {
exclude("com.google.protobuf", "protobuf-java")
}
implementation(libs.slf4j.api)
- implementation(libs.awaitility)
+ implementation(libs.metrics.caffeine)
+ implementation(libs.metrics.core)
compileOnly(libs.guava)
diff --git
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
index e5f2a1a10a..551728e67b 100644
---
a/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
+++
b/catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java
@@ -26,7 +26,9 @@ import static
org.apache.gravitino.file.Fileset.PROPERTY_FILESET_PLACEHOLDER;
import static
org.apache.gravitino.file.Fileset.PROPERTY_LOCATION_PLACEHOLDER_PREFIX;
import static
org.apache.gravitino.file.Fileset.PROPERTY_MULTIPLE_LOCATIONS_PREFIX;
import static org.apache.gravitino.file.Fileset.PROPERTY_SCHEMA_PLACEHOLDER;
+import static org.apache.gravitino.metrics.MetricNames.FILESYSTEM_CACHE;
+import com.codahale.metrics.caffeine.MetricsStatsCounter;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Scheduler;
@@ -89,6 +91,8 @@ import org.apache.gravitino.file.FilesetChange;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.FilesetEntity;
import org.apache.gravitino.meta.SchemaEntity;
+import org.apache.gravitino.metrics.MetricsSystem;
+import org.apache.gravitino.metrics.source.FilesetCatalogMetricsSource;
import org.apache.gravitino.utils.NamespaceUtil;
import org.apache.gravitino.utils.PrincipalUtils;
import org.apache.hadoop.conf.Configuration;
@@ -129,32 +133,13 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
private boolean disableFSOps;
+ private FilesetCatalogMetricsSource catalogMetricsSource;
+
@VisibleForTesting ScheduledThreadPoolExecutor scheduler;
@VisibleForTesting Cache<FileSystemCacheKey, FileSystem> fileSystemCache;
FilesetCatalogOperations(EntityStore store) {
this.store = store;
- scheduler =
- new ScheduledThreadPoolExecutor(
- 1,
- new ThreadFactoryBuilder()
- .setDaemon(true)
- .setNameFormat("file-system-cache-for-fileset" + "-%d")
- .build());
-
- this.fileSystemCache =
- Caffeine.newBuilder()
- .expireAfterAccess(1, TimeUnit.HOURS)
- .removalListener(
- (ignored, value, cause) -> {
- try {
- ((FileSystem) value).close();
- } catch (IOException e) {
- LOG.warn("Failed to close FileSystem instance in cache",
e);
- }
- })
- .scheduler(Scheduler.forScheduledExecutorService(scheduler))
- .build();
}
static class FileSystemCacheKey {
@@ -238,6 +223,14 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
propertiesMetadata
.catalogPropertiesMetadata()
.getOrDefault(config,
FilesetCatalogPropertiesMetadata.DISABLE_FILESYSTEM_OPS);
+
+ MetricsSystem metricsSystem = GravitinoEnv.getInstance().metricsSystem();
+ // Metrics System could be null in UT.
+ if (metricsSystem != null) {
+ this.catalogMetricsSource =
+ new FilesetCatalogMetricsSource(catalogInfo.namespace().toString(),
catalogInfo.name());
+ }
+
if (!disableFSOps) {
String fileSystemProviders =
(String)
@@ -257,9 +250,44 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
this.defaultFileSystemProvider =
FileSystemUtils.getFileSystemProviderByName(
fileSystemProvidersMap, defaultFileSystemProviderName);
+
+ scheduler =
+ new ScheduledThreadPoolExecutor(
+ 1,
+ new ThreadFactoryBuilder()
+ .setDaemon(true)
+ .setNameFormat("file-system-cache-for-fileset" + "-%d")
+ .build());
+
+ Caffeine<Object, Object> cacheBuilder =
+ Caffeine.newBuilder()
+ .expireAfterAccess(1, TimeUnit.HOURS)
+ .removalListener(
+ (ignored, value, cause) -> {
+ try {
+ ((FileSystem) value).close();
+ } catch (IOException e) {
+ LOG.warn("Failed to close FileSystem instance in cache",
e);
+ }
+ })
+ .scheduler(Scheduler.forScheduledExecutorService(scheduler));
+
+ // Metrics System could be null in UT.
+ if (metricsSystem != null) {
+ cacheBuilder.recordStats(
+ () ->
+ new MetricsStatsCounter(
+ catalogMetricsSource.getMetricRegistry(),
FILESYSTEM_CACHE));
+ }
+ this.fileSystemCache = cacheBuilder.build();
}
this.catalogStorageLocations = getAndCheckCatalogStorageLocations(config);
+
+ // Metrics System could be null in UT.
+ if (metricsSystem != null) {
+ metricsSystem.register(catalogMetricsSource);
+ }
}
@Override
@@ -982,6 +1010,12 @@ public class FilesetCatalogOperations extends
ManagedSchemaOperations
});
fileSystemCache.cleanUp();
}
+
+ // Metrics System could be null in UT.
+ MetricsSystem metricsSystem = GravitinoEnv.getInstance().metricsSystem();
+ if (metricsSystem != null) {
+ metricsSystem.unregister(catalogMetricsSource);
+ }
}
private void validateLocationHierarchy(
diff --git
a/core/src/main/java/org/apache/gravitino/metrics/GravitinoSampleBuilder.java
b/core/src/main/java/org/apache/gravitino/metrics/GravitinoSampleBuilder.java
new file mode 100644
index 0000000000..5f4b7f7982
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/metrics/GravitinoSampleBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.gravitino.metrics;
+
+import static
org.apache.gravitino.metrics.source.MetricsSource.GRAVITINO_CATALOG_METRIC_PREFIX;
+
+import io.prometheus.client.Collector;
+import
io.prometheus.client.dropwizard.samplebuilder.CustomMappingSampleBuilder;
+import io.prometheus.client.dropwizard.samplebuilder.MapperConfig;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * GravitinoSampleBuilder is a custom SampleBuilder for Prometheus that
transforms Dropwizard
+ * metrics into Prometheus samples. It is designed to handle Gravitino's
metric naming conventions
+ * for catalog-level metrics.
+ *
+ * <p>The metric names are expected to follow a specific format to be
processed correctly. The
+ * format is: {@code
gravitino-catalog.<provider>.<metalake>.<catalog>.<rest-of-the-metric-name>}.
+ *
+ * <p>This builder extracts the following labels from the metric name:
+ *
+ * <ul>
+ * <li>{@code provider}: The provider of the catalog (e.g., fileset, model,
hive).
+ * <li>{@code metalake}: The name of the metalake.
+ * <li>{@code catalog}: The name of the catalog.
+ * </ul>
+ *
+ * <p>For example, a Dropwizard metric named {@code
+ * gravitino-catalog.fileset.metalake1.catalog1.some-metric} will be converted
to a Prometheus
+ * sample with the metric name {@code gravitino_catalog_some_metric} and the
labels {@code
+ * provider="fileset"}, {@code metalake="metalake1"}, and {@code
catalog="catalog1"}.
+ *
+ * <p>We are not using the default {@link CustomMappingSampleBuilder} with
{@code MapperConfig}
+ * directly because {@code MapperConfig} only supports exact matching of
metric names. For metrics
+ * that contain multiple variable parts, it would require defining a separate
complex rule for each
+ * possible combination, which is not practical. This class uses regular
expressions to dynamically
+ * parse metric names, providing a more flexible and scalable solution.
+ *
+ * <p>Metrics that do not match this pattern will be processed by the default
behavior of {@link
+ * CustomMappingSampleBuilder}.
+ */
+public class GravitinoSampleBuilder extends CustomMappingSampleBuilder {
+ // match pattern:
+ //
gravitino-catalog.<provider>.<metalake>.<catalog>.<rest-of-the-metric-name>
+ private static final Pattern CATALOG_PATTERN =
+ Pattern.compile(
+ Pattern.quote(GRAVITINO_CATALOG_METRIC_PREFIX) +
"\\.([^.]+)\\.([^.]+)\\.([^.]+)\\.(.+)");
+
+ public GravitinoSampleBuilder(List<MapperConfig> mapperConfigs) {
+ super(mapperConfigs);
+ }
+
+ @Override
+ public Collector.MetricFamilySamples.Sample createSample(
+ String dropwizardName,
+ String nameSuffix,
+ List<String> additionalLabelNames,
+ List<String> additionalLabelValues,
+ double value) {
+
+ Matcher matcher = CATALOG_PATTERN.matcher(dropwizardName);
+ if (matcher.matches()) {
+ String provider = matcher.group(1);
+ String metalake = matcher.group(2);
+ String catalog = matcher.group(3);
+ // Replace '.' with '_' in the remaining part to conform to Prometheus
naming conventions
+ String metricNameRest = matcher.group(4).replace('.', '_');
+
+ String prometheusName = GRAVITINO_CATALOG_METRIC_PREFIX + "_" +
metricNameRest;
+
+ List<String> labelNames = new ArrayList<>();
+ labelNames.add("provider");
+ labelNames.add("metalake");
+ labelNames.add("catalog");
+ labelNames.addAll(additionalLabelNames);
+
+ List<String> labelValues = new ArrayList<>();
+ labelValues.add(provider);
+ labelValues.add(metalake);
+ labelValues.add(catalog);
+ labelValues.addAll(additionalLabelValues);
+
+ return new Collector.MetricFamilySamples.Sample(
+ prometheusName, labelNames, labelValues, value);
+ }
+
+ // Fallback to the parent class's default behavior for mismatched metrics
+ return super.createSample(
+ dropwizardName, nameSuffix, additionalLabelNames,
additionalLabelValues, value);
+ }
+}
diff --git a/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
b/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
index d6d09ec13b..b3c8271942 100644
--- a/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
+++ b/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
@@ -25,6 +25,7 @@ public class MetricNames {
public static final String DATASOURCE_ACTIVE_CONNECTIONS =
"datasource.active-connections";
public static final String DATASOURCE_IDLE_CONNECTIONS =
"datasource.idle-connections";
public static final String DATASOURCE_MAX_CONNECTIONS =
"datasource.max-connections";
+ public static final String FILESYSTEM_CACHE = "filesystem-cache";
private MetricNames() {}
}
diff --git a/core/src/main/java/org/apache/gravitino/metrics/MetricsSystem.java
b/core/src/main/java/org/apache/gravitino/metrics/MetricsSystem.java
index fb61f39eff..dde401bfd1 100644
--- a/core/src/main/java/org/apache/gravitino/metrics/MetricsSystem.java
+++ b/core/src/main/java/org/apache/gravitino/metrics/MetricsSystem.java
@@ -151,34 +151,50 @@ public class MetricsSystem implements Closeable {
});
}
- /*
- * Extract a metric name and labels from Dropwizard metrics for Prometheus.
+ /**
+ * Provides rules to extract a metric name and labels from Dropwizard
metrics for Prometheus.
*
- * All extraction rules must be registered with the Prometheus registry
before starting the Prometheus
- * servlet. At times, certain MetricsSources, like HiveCatalogMetricsSource,
may not register with
- * MetricsSystem. Therefore, all rules are consolidated in MetricsSystem
instead of being spread across
- * separate MetricsSources.
+ * <p>All extraction rules must be registered with the Prometheus registry
before starting the
+ * Prometheus servlet. At times, certain MetricsSources, like
HiveCatalogMetricsSource, may not
+ * register with MetricsSystem. Therefore, all rules are consolidated in
MetricsSystem instead of
+ * being spread across separate MetricsSources.
*
- * If a metric name doesn't match any rules, it will be converted to the
Prometheus metrics
+ * <p>If a metric name doesn't match any rules, it will be converted to the
Prometheus metrics
* name format. For example, "ab.c-a.d" transforms into "ab_c_a_d".
*
- * The MapperConfig is utilized to extract Prometheus metricsName and labels
with the following method:
- * `MapperConfig(final String match, final String name, final Map<String,
String> labels)`
- * - `match` is a regex used to match the incoming metric name. It employs a
simplified glob syntax where
- * only '*' is allowed.
- * - `name` is the new metric name, which can contain placeholders to be
replaced with
- * actual values from the incoming metric name. Placeholders are in the
${n} format, where n
- * is the zero-based index of the group to extract from the original
metric name.
- * - `labels` are the labels to be extracted, and they should also contain
placeholders.
- * E.g.:
+ * <p>The {@link MapperConfig} is utilized to extract Prometheus metric
names and labels. The
+ * constructor is {@code MapperConfig(final String match, final String name,
final Map<String,
+ * String> labels)}:
+ *
+ * <ul>
+ * <li>{@code match}: A regex used to match the incoming metric name. It
employs a simplified
+ * glob syntax where only '*' is allowed.
+ * <li>{@code name}: The new metric name, which can contain placeholders
to be replaced with
+ * actual values from the incoming metric name. Placeholders are in
the {@code ${n}} format,
+ * where n is the zero-based index of the group to extract from the
original metric name.
+ * <li>{@code labels}: The labels to be extracted, which can also contain
placeholders.
+ * </ul>
+ *
+ * <p>Example:
+ *
+ * <pre>{@code
* Match: gravitino.dispatcher.*.*
* Name: dispatcher_events_total_${0}
- * Labels: label1: ${1}_t
+ * Labels: {label1: "${1}_t"}
+ * }</pre>
+ *
* A metric "gravitino.dispatcher.sp1.yay" will be converted to a new metric
with name
- * "dispatcher_events_total_sp1" with label {label1: yay_t}.
- * Metric names MUST adhere to the regex [a-zA-Z_:]([a-zA-Z0-9_:])*.
- * Label names MUST adhere to the regex [a-zA-Z_]([a-zA-Z0-9_])*.
- * Label values MAY be any sequence of UTF-8 characters .
+ * "dispatcher_events_total_sp1" and label {@code {label1="yay_t"}}.
+ *
+ * <p>Constraints for Prometheus metrics:
+ *
+ * <ul>
+ * <li>Metric names MUST adhere to the regex {@code
[a-zA-Z_:][a-zA-Z0-9_:]*}.
+ * <li>Label names MUST adhere to the regex {@code [a-zA-Z_][a-zA-Z0-9_]*}.
+ * <li>Label values MAY be any sequence of UTF-8 characters.
+ * </ul>
+ *
+ * @return A list of {@link MapperConfig} rules.
*/
@VisibleForTesting
static List<MapperConfig> getMetricNameAndLabelRules() {
@@ -195,7 +211,7 @@ public class MetricsSystem implements Closeable {
private void registerMetricsToPrometheusRegistry() {
CustomMappingSampleBuilder sampleBuilder =
- new CustomMappingSampleBuilder(getMetricNameAndLabelRules());
+ new GravitinoSampleBuilder(getMetricNameAndLabelRules());
DropwizardExports dropwizardExports = new
DropwizardExports(metricRegistry, sampleBuilder);
dropwizardExports.register(prometheusRegistry);
}
diff --git a/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
b/core/src/main/java/org/apache/gravitino/metrics/source/CatalogMetricsSource.java
similarity index 60%
copy from core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
copy to
core/src/main/java/org/apache/gravitino/metrics/source/CatalogMetricsSource.java
index d6d09ec13b..511d18c06e 100644
--- a/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
+++
b/core/src/main/java/org/apache/gravitino/metrics/source/CatalogMetricsSource.java
@@ -17,14 +17,17 @@
* under the License.
*/
-package org.apache.gravitino.metrics;
+package org.apache.gravitino.metrics.source;
-public class MetricNames {
- public static final String HTTP_PROCESS_DURATION =
"http-request-duration-seconds";
- public static final String SERVER_IDLE_THREAD_NUM =
"http-server.idle-thread.num";
- public static final String DATASOURCE_ACTIVE_CONNECTIONS =
"datasource.active-connections";
- public static final String DATASOURCE_IDLE_CONNECTIONS =
"datasource.idle-connections";
- public static final String DATASOURCE_MAX_CONNECTIONS =
"datasource.max-connections";
+public abstract class CatalogMetricsSource extends MetricsSource {
- private MetricNames() {}
+ public CatalogMetricsSource(String provider, String metalakeName, String
catalogName) {
+ super(
+ String.join(
+ ".",
+ MetricsSource.GRAVITINO_CATALOG_METRIC_PREFIX,
+ provider,
+ metalakeName,
+ catalogName));
+ }
}
diff --git a/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
b/core/src/main/java/org/apache/gravitino/metrics/source/FilesetCatalogMetricsSource.java
similarity index 60%
copy from core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
copy to
core/src/main/java/org/apache/gravitino/metrics/source/FilesetCatalogMetricsSource.java
index d6d09ec13b..4d938a54e1 100644
--- a/core/src/main/java/org/apache/gravitino/metrics/MetricNames.java
+++
b/core/src/main/java/org/apache/gravitino/metrics/source/FilesetCatalogMetricsSource.java
@@ -17,14 +17,11 @@
* under the License.
*/
-package org.apache.gravitino.metrics;
+package org.apache.gravitino.metrics.source;
-public class MetricNames {
- public static final String HTTP_PROCESS_DURATION =
"http-request-duration-seconds";
- public static final String SERVER_IDLE_THREAD_NUM =
"http-server.idle-thread.num";
- public static final String DATASOURCE_ACTIVE_CONNECTIONS =
"datasource.active-connections";
- public static final String DATASOURCE_IDLE_CONNECTIONS =
"datasource.idle-connections";
- public static final String DATASOURCE_MAX_CONNECTIONS =
"datasource.max-connections";
+public class FilesetCatalogMetricsSource extends CatalogMetricsSource {
- private MetricNames() {}
+ public FilesetCatalogMetricsSource(String metalakeName, String catalogName) {
+ super("fileset", metalakeName, catalogName);
+ }
}
diff --git
a/core/src/main/java/org/apache/gravitino/metrics/source/MetricsSource.java
b/core/src/main/java/org/apache/gravitino/metrics/source/MetricsSource.java
index 8289d1de0c..cee2d6afe5 100644
--- a/core/src/main/java/org/apache/gravitino/metrics/source/MetricsSource.java
+++ b/core/src/main/java/org/apache/gravitino/metrics/source/MetricsSource.java
@@ -42,6 +42,7 @@ public abstract class MetricsSource {
public static final String ICEBERG_REST_SERVER_METRIC_NAME =
"iceberg-rest-server";
public static final String GRAVITINO_SERVER_METRIC_NAME = "gravitino-server";
public static final String GRAVITINO_RELATIONAL_STORE_METRIC_NAME =
"gravitino-relational-store";
+ public static final String GRAVITINO_CATALOG_METRIC_PREFIX =
"gravitino-catalog";
public static final String JVM_METRIC_NAME = "jvm";
private final MetricRegistry metricRegistry;
private final String metricsSourceName;
diff --git
a/core/src/test/java/org/apache/gravitino/metrics/TestGravitinoSampleBuilder.java
b/core/src/test/java/org/apache/gravitino/metrics/TestGravitinoSampleBuilder.java
new file mode 100644
index 0000000000..0adc3772d9
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/metrics/TestGravitinoSampleBuilder.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.gravitino.metrics;
+
+import static
org.apache.gravitino.metrics.source.MetricsSource.GRAVITINO_CATALOG_METRIC_PREFIX;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import io.prometheus.client.Collector;
+import io.prometheus.client.dropwizard.samplebuilder.MapperConfig;
+import java.util.Collections;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class TestGravitinoSampleBuilder {
+
+ private static GravitinoSampleBuilder sampleBuilder;
+
+ @BeforeAll
+ public static void setUp() {
+ List<MapperConfig> mapperConfigs =
+ Collections.singletonList(
+ new MapperConfig(
+ "test.default.*", "test_default_${0}", ImmutableMap.of("name",
"${0}")));
+ sampleBuilder = new GravitinoSampleBuilder(mapperConfigs);
+ }
+
+ @Test
+ public void testCreateSampleWithCatalogMetric() {
+ String dropwizardName =
"gravitino-catalog.hive.metalake1.catalog1.some.metric.count";
+ double value = 10.0;
+
+ Collector.MetricFamilySamples.Sample sample =
+ sampleBuilder.createSample(
+ dropwizardName, "", Collections.emptyList(),
Collections.emptyList(), value);
+
+ Assertions.assertEquals(GRAVITINO_CATALOG_METRIC_PREFIX +
"_some_metric_count", sample.name);
+ Assertions.assertEquals(ImmutableList.of("provider", "metalake",
"catalog"), sample.labelNames);
+ Assertions.assertEquals(ImmutableList.of("hive", "metalake1", "catalog1"),
sample.labelValues);
+ Assertions.assertEquals(value, sample.value);
+ }
+
+ @Test
+ public void testCreateSampleWithCatalogMetricAndAdditionalLabels() {
+ String dropwizardName =
"gravitino-catalog.jdbc.metalake2.catalog2.another.metric";
+ double value = 20.0;
+ List<String> additionalLabelNames = Collections.singletonList("type");
+ List<String> additionalLabelValues = Collections.singletonList("gauge");
+
+ Collector.MetricFamilySamples.Sample sample =
+ sampleBuilder.createSample(
+ dropwizardName, "", additionalLabelNames, additionalLabelValues,
value);
+
+ Assertions.assertEquals(GRAVITINO_CATALOG_METRIC_PREFIX +
"_another_metric", sample.name);
+ Assertions.assertEquals(
+ ImmutableList.of("provider", "metalake", "catalog", "type"),
sample.labelNames);
+ Assertions.assertEquals(
+ ImmutableList.of("jdbc", "metalake2", "catalog2", "gauge"),
sample.labelValues);
+ Assertions.assertEquals(value, sample.value);
+ }
+
+ @Test
+ public void testCreateSampleWithNonCatalogMetricMatchingParentRule() {
+ String dropwizardName = "test.default.metric1";
+ double value = 30.0;
+
+ Collector.MetricFamilySamples.Sample sample =
+ sampleBuilder.createSample(
+ dropwizardName, "", Collections.emptyList(),
Collections.emptyList(), value);
+
+ Assertions.assertEquals("test_default_metric1", sample.name);
+ Assertions.assertEquals(Collections.singletonList("name"),
sample.labelNames);
+ Assertions.assertEquals(Collections.singletonList("metric1"),
sample.labelValues);
+ Assertions.assertEquals(value, sample.value);
+ }
+
+ @Test
+ public void testCreateSampleWithNonCatalogMetricNotMatchingAnyRule() {
+ String dropwizardName = "unmatched.metric.name";
+ double value = 40.0;
+
+ Collector.MetricFamilySamples.Sample sample =
+ sampleBuilder.createSample(
+ dropwizardName, "", Collections.emptyList(),
Collections.emptyList(), value);
+
+ Assertions.assertEquals("unmatched_metric_name", sample.name);
+ Assertions.assertTrue(sample.labelNames.isEmpty());
+ Assertions.assertTrue(sample.labelValues.isEmpty());
+ Assertions.assertEquals(value, sample.value);
+ }
+}
diff --git a/docs/metrics.md b/docs/metrics.md
index 85738fe7e7..d9ae09d27e 100644
--- a/docs/metrics.md
+++ b/docs/metrics.md
@@ -49,3 +49,15 @@ Metrics with the `gravitino-server` prefix pertain to the
Gravitino server, whil
JVM metrics source uses [JVM
instrumentation](https://metrics.dropwizard.io/4.2.0/manual/jvm.html) with
BufferPoolMetricSet, GarbageCollectorMetricSet, and MemoryUsageGaugeSet.
These metrics start with the `jvm` prefix, like `jvm.heap.used` in JSON
format, `jvm_heap_used` in Prometheus format.
+
+#### Catalog metrics
+
+Catalog metrics provide the metrics from different catalog instances.
+All the catalog metrics start with the `gravitino-catalog` prefix in
Prometheus format and with labels `provider`, `metalake`, and `catalog` to
distinguish different catalog instances.
+
+For example, you can get Prometheus metrics for a fileset catalog named
`test_catalog` under a metalake named `test_metalake` in the Gravitino server
as follows:
+
+```text
+gravitino-catalog_filesystem-cache_hits{provider="fileset",metalake="test_metalake",catalog="test_catalog",}
0.0
+gravitino-catalog_filesystem-cache_misses{provider="fileset",metalake="test_metalake",catalog="test_catalog",}
0.0
+```
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 89dbbec979..d0779e0ee6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -253,6 +253,7 @@ metrics-core = { group = "io.dropwizard.metrics", name =
"metrics-core", version
metrics-jersey2 = { group = "io.dropwizard.metrics", name = "metrics-jersey2",
version.ref = "metrics" }
metrics-jvm = { group = "io.dropwizard.metrics", name = "metrics-jvm",
version.ref = "metrics" }
metrics-jmx = { group = "io.dropwizard.metrics", name = "metrics-jmx",
version.ref = "metrics" }
+metrics-caffeine = { group = "io.dropwizard.metrics", name =
"metrics-caffeine", version.ref = "metrics" }
jline-terminal = { group = "org.jline", name = "jline-terminal", version.ref =
"jline" }
okhttp3-loginterceptor = { group = "com.squareup.okhttp3", name =
"logging-interceptor", version.ref = "okhttp3" }
opencsv = {group = "net.sf.opencsv", name = "opencsv", version.ref = "opencsv"}