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"}

Reply via email to