This is an automated email from the ASF dual-hosted git repository.

jin pushed a commit to branch pd-store
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph.git

commit 176637c47a9d6ea8ad9433e52190a11c5ea66c1a
Author: SunnyBoy-WYH <[email protected]>
AuthorDate: Sun Oct 8 20:21:09 2023 +0800

    feat(api): support metric API Prometheus format & add statistic metric api 
(#2286)
---
 .../main/java/org/apache/hugegraph/api/API.java    |   3 +-
 .../hugegraph/api/filter/AccessLogFilter.java      |  82 ++++++
 .../apache/hugegraph/api/filter/PathFilter.java    |  40 +++
 .../apache/hugegraph/api/metrics/MetricsAPI.java   | 316 +++++++++++++++++++--
 .../org/apache/hugegraph/metrics/MetricsKeys.java  |  40 +++
 .../org/apache/hugegraph/metrics/MetricsUtil.java  | 165 ++++++++++-
 .../org/apache/hugegraph/api/MetricsApiTest.java   |  34 ++-
 7 files changed, 643 insertions(+), 37 deletions(-)

diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
index 99fe67e5b..e57f6739d 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
@@ -27,6 +27,7 @@ import org.apache.hugegraph.HugeException;
 import org.apache.hugegraph.HugeGraph;
 import org.apache.hugegraph.core.GraphManager;
 import org.apache.hugegraph.define.Checkable;
+import org.apache.hugegraph.exception.NotFoundException;
 import org.apache.hugegraph.metrics.MetricsUtil;
 import org.apache.hugegraph.util.E;
 import org.apache.hugegraph.util.InsertionOrderUtil;
@@ -38,7 +39,6 @@ import com.codahale.metrics.Meter;
 import com.google.common.collect.ImmutableMap;
 
 import jakarta.ws.rs.ForbiddenException;
-import jakarta.ws.rs.NotFoundException;
 import jakarta.ws.rs.NotSupportedException;
 import jakarta.ws.rs.core.MediaType;
 
@@ -49,6 +49,7 @@ public class API {
     public static final String APPLICATION_JSON = MediaType.APPLICATION_JSON;
     public static final String APPLICATION_JSON_WITH_CHARSET =
                                APPLICATION_JSON + ";charset=" + CHARSET;
+    public static final String APPLICATION_TEXT_WITH_CHARSET = 
MediaType.TEXT_PLAIN + ";charset=" + CHARSET;
     public static final String JSON = MediaType.APPLICATION_JSON_TYPE
                                                .getSubtype();
     public static final String ACTION_APPEND = "append";
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AccessLogFilter.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AccessLogFilter.java
new file mode 100644
index 000000000..ba9c98118
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AccessLogFilter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.hugegraph.api.filter;
+
+import static org.apache.hugegraph.api.filter.PathFilter.REQUEST_TIME;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_FAILED_COUNTER;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_RESPONSE_TIME_HISTOGRAM;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_SUCCESS_COUNTER;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_TOTAL_COUNTER;
+
+import java.io.IOException;
+
+import org.apache.hugegraph.metrics.MetricsUtil;
+
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import jakarta.ws.rs.container.ContainerResponseFilter;
+import jakarta.ws.rs.ext.Provider;
+
+
+@Provider
+@Singleton
+public class AccessLogFilter implements ContainerResponseFilter {
+
+    private static final String DELIMETER = "/";
+
+    /**
+     * Use filter to log request info
+     *
+     * @param requestContext  requestContext
+     * @param responseContext responseContext
+     */
+    @Override
+    public void filter(ContainerRequestContext requestContext, 
ContainerResponseContext responseContext) throws IOException {
+        // Grab corresponding request / response info from context;
+        String method = requestContext.getRequest().getMethod();
+        String path = requestContext.getUriInfo().getPath();
+        String metricsName = join(path, method);
+
+        MetricsUtil.registerCounter(join(metricsName, 
METRICS_PATH_TOTAL_COUNTER)).inc();
+        if (statusOk(responseContext.getStatus())) {
+            MetricsUtil.registerCounter(join(metricsName, 
METRICS_PATH_SUCCESS_COUNTER)).inc();
+        } else {
+            MetricsUtil.registerCounter(join(metricsName, 
METRICS_PATH_FAILED_COUNTER)).inc();
+        }
+
+        // get responseTime
+        Object requestTime = requestContext.getProperty(REQUEST_TIME);
+        if(requestTime!=null){
+            long now = System.currentTimeMillis();
+            long responseTime = (now - (long)requestTime);
+
+            MetricsUtil.registerHistogram(
+                               join(metricsName, 
METRICS_PATH_RESPONSE_TIME_HISTOGRAM))
+                       .update(responseTime);
+        }
+    }
+
+    private String join(String path1, String path2) {
+        return String.join(DELIMETER, path1, path2);
+    }
+
+    private boolean statusOk(int status){
+        return status == 200 || status == 201 || status == 202;
+    }
+}
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/PathFilter.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/PathFilter.java
new file mode 100644
index 000000000..3414d6831
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/PathFilter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.hugegraph.api.filter;
+
+import java.io.IOException;
+
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.PreMatching;
+import jakarta.ws.rs.ext.Provider;
+
+@Provider
+@Singleton
+@PreMatching
+public class PathFilter implements ContainerRequestFilter {
+
+    public static final String REQUEST_TIME = "request_time";
+
+    @Override
+    public void filter(ContainerRequestContext context)
+            throws IOException {
+        context.setProperty(REQUEST_TIME, System.currentTimeMillis());
+    }
+}
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
index 6df4f6453..f74286b5f 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
@@ -19,33 +19,66 @@ package org.apache.hugegraph.api.metrics;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.hugegraph.metrics.MetricsUtil.COUNT_ATTR;
+import static org.apache.hugegraph.metrics.MetricsUtil.END_LSTR;
+import static org.apache.hugegraph.metrics.MetricsUtil.FIFT_MIN_RATE_ATRR;
+import static org.apache.hugegraph.metrics.MetricsUtil.FIVE_MIN_RATE_ATRR;
+import static org.apache.hugegraph.metrics.MetricsUtil.GAUGE_TYPE;
+import static org.apache.hugegraph.metrics.MetricsUtil.HISTOGRAM_TYPE;
+import static org.apache.hugegraph.metrics.MetricsUtil.LEFT_NAME_STR;
+import static org.apache.hugegraph.metrics.MetricsUtil.MEAN_RATE_ATRR;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_FAILED_COUNTER;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_RESPONSE_TIME_HISTOGRAM;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_SUCCESS_COUNTER;
+import static 
org.apache.hugegraph.metrics.MetricsUtil.METRICS_PATH_TOTAL_COUNTER;
+import static org.apache.hugegraph.metrics.MetricsUtil.ONE_MIN_RATE_ATRR;
+import static org.apache.hugegraph.metrics.MetricsUtil.PROM_HELP_NAME;
+import static org.apache.hugegraph.metrics.MetricsUtil.RIGHT_NAME_STR;
+import static org.apache.hugegraph.metrics.MetricsUtil.SPACE_STR;
+import static org.apache.hugegraph.metrics.MetricsUtil.STR_HELP;
+import static org.apache.hugegraph.metrics.MetricsUtil.STR_TYPE;
+import static org.apache.hugegraph.metrics.MetricsUtil.UNTYPED;
+import static org.apache.hugegraph.metrics.MetricsUtil.VERSION_STR;
+import static org.apache.hugegraph.metrics.MetricsUtil.exportSnapshot;
+import static org.apache.hugegraph.metrics.MetricsUtil.replaceDotDashInKey;
+import static org.apache.hugegraph.metrics.MetricsUtil.replaceSlashInKey;
 
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.security.RolesAllowed;
-import jakarta.inject.Singleton;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.core.Context;
-
+import org.apache.hugegraph.HugeGraph;
+import org.apache.hugegraph.api.API;
+import org.apache.hugegraph.backend.store.BackendMetrics;
 import org.apache.hugegraph.core.GraphManager;
+import org.apache.hugegraph.metrics.MetricsKeys;
 import org.apache.hugegraph.metrics.MetricsModule;
+import org.apache.hugegraph.metrics.MetricsUtil;
 import org.apache.hugegraph.metrics.ServerReporter;
 import org.apache.hugegraph.metrics.SystemMetrics;
-import org.slf4j.Logger;
-
-import org.apache.hugegraph.HugeGraph;
-import org.apache.hugegraph.api.API;
-import org.apache.hugegraph.backend.store.BackendMetrics;
 import org.apache.hugegraph.util.InsertionOrderUtil;
 import org.apache.hugegraph.util.JsonUtil;
 import org.apache.hugegraph.util.Log;
+import org.apache.hugegraph.version.ApiVersion;
+import org.apache.tinkerpop.gremlin.server.util.MetricManager;
+import org.slf4j.Logger;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
 import com.codahale.metrics.Metric;
 import com.codahale.metrics.annotation.Timed;
 
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.Context;
+
 @Singleton
 @Path("metrics")
 @Tag(name = "MetricsAPI")
@@ -53,12 +86,14 @@ public class MetricsAPI extends API {
 
     private static final Logger LOG = Log.logger(MetricsAPI.class);
 
-    private SystemMetrics systemMetrics;
+    private static final String JSON_STR = "json";
 
     static {
         JsonUtil.registerModule(new MetricsModule(SECONDS, MILLISECONDS, 
false));
     }
 
+    private final SystemMetrics systemMetrics;
+
     public MetricsAPI() {
         this.systemMetrics = new SystemMetrics();
     }
@@ -94,21 +129,6 @@ public class MetricsAPI extends API {
         return JsonUtil.toJson(results);
     }
 
-    @GET
-    @Timed
-    @Produces(APPLICATION_JSON_WITH_CHARSET)
-    @RolesAllowed({"admin", "$owner= $action=metrics_read"})
-    public String all() {
-        ServerReporter reporter = ServerReporter.instance();
-        Map<String, Map<String, ? extends Metric>> result = new 
LinkedHashMap<>();
-        result.put("gauges", reporter.gauges());
-        result.put("counters", reporter.counters());
-        result.put("histograms", reporter.histograms());
-        result.put("meters", reporter.meters());
-        result.put("timers", reporter.timers());
-        return JsonUtil.toJson(result);
-    }
-
     @GET
     @Timed
     @Path("gauges")
@@ -158,4 +178,242 @@ public class MetricsAPI extends API {
         ServerReporter reporter = ServerReporter.instance();
         return JsonUtil.toJson(reporter.timers());
     }
+
+    @GET
+    @Timed
+    @Produces(APPLICATION_TEXT_WITH_CHARSET)
+    @RolesAllowed({"admin", "$owner= $action=metrics_read"})
+    public String all(@Context GraphManager manager,
+                      @QueryParam("type") String type) {
+        if (type != null && type.equals(JSON_STR)) {
+            return baseMetricAll();
+        } else {
+            return baseMetricPrometheusAll();
+        }
+    }
+
+    @GET
+    @Path("statistics")
+    @Timed
+    @Produces(APPLICATION_TEXT_WITH_CHARSET)
+    @RolesAllowed({"admin", "$owner= $action=metrics_read"})
+    public String statistics(@QueryParam("type") String type) {
+        Map<String, Map<String, Object>> metricMap = statistics();
+
+        if (type != null && type.equals(JSON_STR)) {
+            return JsonUtil.toJson(metricMap);
+        }
+        return statisticsProm(metricMap);
+    }
+
+    public String baseMetricAll() {
+        ServerReporter reporter = ServerReporter.instance();
+        Map<String, Map<String, ? extends Metric>> result = new 
LinkedHashMap<>();
+        result.put("gauges", reporter.gauges());
+        result.put("counters", reporter.counters());
+        result.put("histograms", reporter.histograms());
+        result.put("meters", reporter.meters());
+        result.put("timers", reporter.timers());
+        return JsonUtil.toJson(result);
+    }
+
+    private String baseMetricPrometheusAll() {
+        StringBuilder promMetric = new StringBuilder();
+        ServerReporter reporter = ServerReporter.instance();
+        String helpName = PROM_HELP_NAME;
+        // build version info
+        promMetric.append(STR_HELP)
+                  .append(helpName).append(END_LSTR);
+        promMetric.append(STR_TYPE)
+                  .append(helpName)
+                  .append(SPACE_STR + UNTYPED + END_LSTR);
+        promMetric.append(helpName)
+                  .append(VERSION_STR)
+                  .append(ApiVersion.VERSION.toString()).append("\",}")
+                  .append(SPACE_STR + "1.0" + END_LSTR);
+
+        // build gauges metric info
+        for (String key : reporter.gauges().keySet()) {
+            final Gauge<?> gauge
+                    = reporter.gauges().get(key);
+            if (gauge != null) {
+                helpName = replaceDotDashInKey(key);
+                promMetric.append(STR_HELP)
+                          .append(helpName).append(END_LSTR);
+                promMetric.append(STR_TYPE)
+                          .append(helpName).append(SPACE_STR + GAUGE_TYPE + 
END_LSTR);
+                promMetric.append(helpName)
+                          .append(SPACE_STR + gauge.getValue() + END_LSTR);
+            }
+        }
+
+        // build histograms metric info
+        for (String histogramkey : reporter.histograms().keySet()) {
+            final Histogram histogram = 
reporter.histograms().get(histogramkey);
+            if (histogram != null) {
+                helpName = replaceDotDashInKey(histogramkey);
+                promMetric.append(STR_HELP)
+                          .append(helpName).append(END_LSTR);
+                promMetric.append(STR_TYPE)
+                          .append(helpName)
+                          .append(SPACE_STR + HISTOGRAM_TYPE + END_LSTR);
+
+                promMetric.append(helpName)
+                          .append(COUNT_ATTR)
+                          .append(histogram.getCount() + END_LSTR);
+                promMetric.append(
+                        exportSnapshot(helpName, histogram.getSnapshot()));
+            }
+        }
+
+        // build meters metric info
+        for (String meterkey : reporter.meters().keySet()) {
+            final Meter metric = reporter.meters().get(meterkey);
+            if (metric != null) {
+                helpName = replaceDotDashInKey(meterkey);
+                promMetric.append(STR_HELP)
+                          .append(helpName).append(END_LSTR);
+                promMetric.append(STR_TYPE)
+                          .append(helpName)
+                          .append(SPACE_STR + HISTOGRAM_TYPE + END_LSTR);
+
+                promMetric.append(helpName)
+                          .append(COUNT_ATTR)
+                          .append(metric.getCount() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(MEAN_RATE_ATRR)
+                          .append(metric.getMeanRate() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(ONE_MIN_RATE_ATRR)
+                          .append(metric.getOneMinuteRate() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(FIVE_MIN_RATE_ATRR)
+                          .append(metric.getFiveMinuteRate() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(FIFT_MIN_RATE_ATRR)
+                          .append(metric.getFifteenMinuteRate() + END_LSTR);
+            }
+        }
+
+        // build timer metric info
+        for (String timerkey : reporter.timers().keySet()) {
+            final com.codahale.metrics.Timer timer = reporter.timers()
+                                                             .get(timerkey);
+            if (timer != null) {
+                helpName = replaceDotDashInKey(timerkey);
+                promMetric.append(STR_HELP)
+                          .append(helpName).append(END_LSTR);
+                promMetric.append(STR_TYPE)
+                          .append(helpName)
+                          .append(SPACE_STR + HISTOGRAM_TYPE + END_LSTR);
+
+                promMetric.append(helpName)
+                          .append(COUNT_ATTR)
+                          .append(timer.getCount() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(ONE_MIN_RATE_ATRR)
+                          .append(timer.getOneMinuteRate() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(FIVE_MIN_RATE_ATRR)
+                          .append(timer.getFiveMinuteRate() + END_LSTR);
+                promMetric.append(helpName)
+                          .append(FIFT_MIN_RATE_ATRR)
+                          .append(timer.getFifteenMinuteRate() + END_LSTR);
+                promMetric.append(
+                        exportSnapshot(helpName, timer.getSnapshot()));
+            }
+        }
+
+        MetricsUtil.writePrometheusFormat(promMetric, 
MetricManager.INSTANCE.getRegistry());
+
+        return promMetric.toString();
+    }
+
+    private Map<String, Map<String, Object>> statistics() {
+        Map<String, Map<String, Object>> metricsMap = new HashMap<>();
+        ServerReporter reporter = ServerReporter.instance();
+        for (Map.Entry<String, Histogram> entry : 
reporter.histograms().entrySet()) {
+            // entryKey = path/method/responseTimeHistogram
+            String entryKey = entry.getKey();
+            String[] split = entryKey.split("/");
+            String lastWord = split[split.length - 1];
+            if (!lastWord.equals(METRICS_PATH_RESPONSE_TIME_HISTOGRAM)) {
+                // original metrics dont report
+                continue;
+            }
+            // metricsName = path/method
+            String metricsName =
+                    entryKey.substring(0, entryKey.length() - 
lastWord.length() - 1);
+
+            Counter totalCounter = reporter.counters().get(
+                    joinWithSlash(metricsName, METRICS_PATH_TOTAL_COUNTER));
+            Counter failedCounter = reporter.counters().get(
+                    joinWithSlash(metricsName, METRICS_PATH_FAILED_COUNTER));
+            Counter successCounter = reporter.counters().get(
+                    joinWithSlash(metricsName, METRICS_PATH_SUCCESS_COUNTER));
+
+
+            Histogram histogram = entry.getValue();
+            Map<String, Object> entryMetricsMap = new HashMap<>();
+            entryMetricsMap.put(MetricsKeys.MAX_RESPONSE_TIME.name(),
+                                histogram.getSnapshot().getMax());
+            entryMetricsMap.put(MetricsKeys.MEAN_RESPONSE_TIME.name(),
+                                histogram.getSnapshot().getMean());
+
+            entryMetricsMap.put(MetricsKeys.TOTAL_REQUEST.name(),
+                                totalCounter.getCount());
+
+            if (failedCounter == null) {
+                entryMetricsMap.put(MetricsKeys.FAILED_REQUEST.name(), 0);
+            } else {
+                entryMetricsMap.put(MetricsKeys.FAILED_REQUEST.name(),
+                                    failedCounter.getCount());
+            }
+
+            if (successCounter == null) {
+                entryMetricsMap.put(MetricsKeys.SUCCESS_REQUEST.name(), 0);
+            } else {
+                entryMetricsMap.put(MetricsKeys.SUCCESS_REQUEST.name(),
+                                    successCounter.getCount());
+            }
+
+            metricsMap.put(metricsName, entryMetricsMap);
+
+        }
+        return metricsMap;
+    }
+
+    private String statisticsProm(Map<String, Map<String, Object>> metricMap) {
+        StringBuilder promMetric = new StringBuilder();
+
+        // build version info
+        promMetric.append(STR_HELP)
+                  .append(PROM_HELP_NAME).append(END_LSTR);
+        promMetric.append(STR_TYPE)
+                  .append(PROM_HELP_NAME)
+                  .append(SPACE_STR + UNTYPED + END_LSTR);
+        promMetric.append(PROM_HELP_NAME)
+                  .append(VERSION_STR)
+                  .append(ApiVersion.VERSION.toString()).append("\",}")
+                  .append(SPACE_STR + "1.0" + END_LSTR);
+
+        for (String methodKey : metricMap.keySet()) {
+            String metricName = replaceSlashInKey(methodKey);
+            promMetric.append(STR_HELP)
+                      .append(metricName).append(END_LSTR);
+            promMetric.append(STR_TYPE)
+                      .append(metricName).append(SPACE_STR + GAUGE_TYPE + 
END_LSTR);
+            Map<String, Object> itemMetricMap = metricMap.get(methodKey);
+            for (String labelName : itemMetricMap.keySet()) {
+                
promMetric.append(metricName).append(LEFT_NAME_STR).append(labelName)
+                          
.append(RIGHT_NAME_STR).append(itemMetricMap.get(labelName))
+                          .append(END_LSTR);
+            }
+        }
+        return promMetric.toString();
+    }
+
+    private String joinWithSlash(String path1, String path2) {
+        return String.join("/", path1, path2);
+    }
 }
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsKeys.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsKeys.java
new file mode 100644
index 000000000..1cda15c82
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsKeys.java
@@ -0,0 +1,40 @@
+/*
+ * 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.hugegraph.metrics;
+
+public enum MetricsKeys {
+
+    MAX_RESPONSE_TIME(1, "max_response_time"),
+
+    MEAN_RESPONSE_TIME(2, "mean_response_time"),
+
+    TOTAL_REQUEST(3, "total_request"),
+
+    FAILED_REQUEST(4, "failed_request"),
+
+    SUCCESS_REQUEST(5, "success_request");
+
+    private final byte code;
+    private final String name;
+
+    MetricsKeys(int code, String name) {
+        assert code < 256;
+        this.code = (byte) code;
+        this.name = name;
+    }
+}
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
index fabd1df7b..bb411f927 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
@@ -24,12 +24,45 @@ import com.codahale.metrics.Gauge;
 import com.codahale.metrics.Histogram;
 import com.codahale.metrics.Meter;
 import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Snapshot;
 import com.codahale.metrics.Timer;
 
 public class MetricsUtil {
 
-    private static final MetricRegistry REGISTRY =
-                                        MetricManager.INSTANCE.getRegistry();
+    public static final String METRICS_PATH_TOTAL_COUNTER = "TOTAL_COUNTER";
+    public static final String METRICS_PATH_FAILED_COUNTER = "FAILED_COUNTER";
+    public static final String METRICS_PATH_SUCCESS_COUNTER = 
"SUCCESS_COUNTER";
+    public static final String METRICS_PATH_RESPONSE_TIME_HISTOGRAM =
+            "RESPONSE_TIME_HISTOGRAM";
+    public static final String P75_ATTR = "{name=\"p75\",} ";
+    public static final String P95_ATTR = "{name=\"p95\",} ";
+    public static final String P98_ATTR = "{name=\"p98\",} ";
+    public static final String P99_ATTR = "{name=\"p99\",} ";
+    public static final String P999_ATTR = "{name=\"p999\",} ";
+    public static final String MEAN_RATE_ATRR = "{name=\"mean_rate\",} ";
+    public static final String ONE_MIN_RATE_ATRR = "{name=\"m1_rate\",} ";
+    public static final String FIVE_MIN_RATE_ATRR = "{name=\"m5_rate\",} ";
+    public static final String FIFT_MIN_RATE_ATRR = "{name=\"m15_rate\",} ";
+    public static final MetricRegistry REGISTRY = 
MetricManager.INSTANCE.getRegistry();
+    public static final String STR_HELP = "# HELP ";
+    public static final String STR_TYPE = "# TYPE ";
+    public static final String HISTOGRAM_TYPE = "histogram";
+    public static final String UNTYPED = "untyped";
+    public static final String GAUGE_TYPE = "gauge";
+    public static final String END_LSTR = "\n";
+    public static final String SPACE_STR = " ";
+    public static final String VERSION_STR = "{version=\"";
+    public static final String COUNT_ATTR = "{name=\"count\",} ";
+    public static final String MIN_ATTR = "{name=\"min\",} ";
+    public static final String MAX_ATTR = "{name=\"max\",} ";
+    public static final String MEAN_ATTR = "{name=\"mean\",} ";
+    public static final String STDDEV_ATTR = "{name=\"stddev\",} ";
+    public static final String P50_ATTR = "{name=\"p50\",} ";
+
+    public static final String LEFT_NAME_STR = "{name=";
+    public static final String RIGHT_NAME_STR = ",} ";
+    public static final String PROM_HELP_NAME = "hugegraph_info";
+
 
     public static <T> Gauge<T> registerGauge(Class<?> clazz, String name,
                                              Gauge<T> gauge) {
@@ -40,10 +73,18 @@ public class MetricsUtil {
         return REGISTRY.counter(MetricRegistry.name(clazz, name));
     }
 
+    public static Counter registerCounter(String name) {
+        return REGISTRY.counter(MetricRegistry.name(name));
+    }
+
     public static Histogram registerHistogram(Class<?> clazz, String name) {
         return REGISTRY.histogram(MetricRegistry.name(clazz, name));
     }
 
+    public static Histogram registerHistogram(String name) {
+        return REGISTRY.histogram(name);
+    }
+
     public static Meter registerMeter(Class<?> clazz, String name) {
         return REGISTRY.meter(MetricRegistry.name(clazz, name));
     }
@@ -51,4 +92,124 @@ public class MetricsUtil {
     public static Timer registerTimer(Class<?> clazz, String name) {
         return REGISTRY.timer(MetricRegistry.name(clazz, name));
     }
+
+    public static String replaceDotDashInKey(String orgKey) {
+        return orgKey.replace(".", "_").replace("-", "_");
+    }
+
+    public static String replaceSlashInKey(String orgKey) {
+        return orgKey.replace("/", "_");
+    }
+
+    public static void writePrometheusFormat(StringBuilder promeMetrics, 
MetricRegistry registry) {
+        // gauges
+        registry.getGauges().forEach((key, gauge) -> {
+            if (gauge != null) {
+                String helpName = replaceDotDashInKey(key);
+                promeMetrics.append(STR_HELP)
+                            .append(helpName).append(END_LSTR);
+                promeMetrics.append(STR_TYPE)
+                            .append(helpName).append(SPACE_STR + GAUGE_TYPE + 
END_LSTR);
+                
promeMetrics.append(helpName).append(SPACE_STR).append(gauge.getValue())
+                            .append(END_LSTR);
+            }
+        });
+
+        // histograms
+        registry.getHistograms().forEach((key, histogram) -> {
+            if (histogram != null) {
+                String helpName = replaceDotDashInKey(key);
+                promeMetrics.append(STR_HELP)
+                            .append(helpName).append(END_LSTR);
+                promeMetrics.append(STR_TYPE)
+                            .append(helpName)
+                            .append(SPACE_STR + HISTOGRAM_TYPE + END_LSTR);
+
+                promeMetrics.append(helpName)
+                            
.append(COUNT_ATTR).append(histogram.getCount()).append(END_LSTR);
+                promeMetrics.append(
+                        exportSnapshot(helpName, histogram.getSnapshot()));
+            }
+        });
+
+        // meters
+        registry.getMeters().forEach((key, metric) -> {
+            if (metric != null) {
+                String helpName = replaceDotDashInKey(key);
+                promeMetrics.append(STR_HELP)
+                            .append(helpName).append(END_LSTR);
+                promeMetrics.append(STR_TYPE)
+                            .append(helpName)
+                            .append(SPACE_STR + HISTOGRAM_TYPE + END_LSTR);
+
+                promeMetrics.append(helpName)
+                            
.append(COUNT_ATTR).append(metric.getCount()).append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(MEAN_RATE_ATRR).append(metric.getMeanRate()).append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(ONE_MIN_RATE_ATRR).append(metric.getOneMinuteRate())
+                            .append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(FIVE_MIN_RATE_ATRR).append(metric.getFiveMinuteRate())
+                            .append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(FIFT_MIN_RATE_ATRR).append(metric.getFifteenMinuteRate())
+                            .append(END_LSTR);
+            }
+        });
+
+        // timer
+        registry.getTimers().forEach((key, timer) -> {
+            if (timer != null) {
+                String helpName = replaceDotDashInKey(key);
+                promeMetrics.append(STR_HELP)
+                            .append(helpName).append(END_LSTR);
+                promeMetrics.append(STR_TYPE)
+                            .append(helpName)
+                            .append(SPACE_STR + HISTOGRAM_TYPE + END_LSTR);
+
+                promeMetrics.append(helpName)
+                            
.append(COUNT_ATTR).append(timer.getCount()).append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(ONE_MIN_RATE_ATRR).append(timer.getOneMinuteRate())
+                            .append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(FIVE_MIN_RATE_ATRR).append(timer.getFiveMinuteRate())
+                            .append(END_LSTR);
+                promeMetrics.append(helpName)
+                            
.append(FIFT_MIN_RATE_ATRR).append(timer.getFifteenMinuteRate())
+                            .append(END_LSTR);
+                promeMetrics.append(
+                        exportSnapshot(helpName, timer.getSnapshot()));
+            }
+        });
+    }
+
+    public static String exportSnapshot(final String helpName, final Snapshot 
snapshot) {
+        if (snapshot == null) {
+            return "";
+        }
+        StringBuilder snapMetrics = new StringBuilder();
+        snapMetrics.append(helpName)
+                   
.append(MIN_ATTR).append(snapshot.getMin()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(MAX_ATTR).append(snapshot.getMax()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(MEAN_ATTR).append(snapshot.getMean()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(STDDEV_ATTR).append(snapshot.getStdDev()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(P50_ATTR).append(snapshot.getMedian()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(P75_ATTR).append(snapshot.get75thPercentile()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(P95_ATTR).append(snapshot.get95thPercentile()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(P98_ATTR).append(snapshot.get98thPercentile()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(P99_ATTR).append(snapshot.get99thPercentile()).append(END_LSTR);
+        snapMetrics.append(helpName)
+                   
.append(P999_ATTR).append(snapshot.get999thPercentile()).append(END_LSTR);
+        return snapMetrics.toString();
+    }
 }
diff --git 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
index 499103c17..cce5af30c 100644
--- 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
+++ 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
@@ -17,20 +17,24 @@
 
 package org.apache.hugegraph.api;
 
+import java.util.HashMap;
 import java.util.Map;
 
-import jakarta.ws.rs.core.Response;
+import org.apache.hugegraph.testutil.Assert;
 import org.junit.Test;
 
-import org.apache.hugegraph.testutil.Assert;
+import jakarta.ws.rs.core.Response;
 
 public class MetricsApiTest extends BaseApiTest {
 
-    private static String path = "/metrics";
+    private static final String path = "/metrics";
+    private static final String statisticsPath = path + "/statistics";
 
     @Test
-    public void testMetricsAll() {
-        Response r = client().get(path);
+    public void testBaseMetricsAll() {
+        Map<String, Object> params = new HashMap<>();
+        params.put("type", "json");
+        Response r = client().get(path, params);
         String result = assertResponseStatus(200, r);
         assertJsonContains(result, "gauges");
         assertJsonContains(result, "counters");
@@ -39,6 +43,26 @@ public class MetricsApiTest extends BaseApiTest {
         assertJsonContains(result, "timers");
     }
 
+    @Test
+    public void testBaseMetricsPromAll() {
+        Response r = client().get(path);
+        assertResponseStatus(200, r);
+    }
+
+    @Test
+    public void testStatisticsMetricsAll() {
+        Map<String, String> params = new HashMap<>();
+        params.put("type", "json");
+        Response r = client().get(path);
+        assertResponseStatus(200, r);
+    }
+
+    @Test
+    public void testStatisticsMetricsPromAll() {
+        Response r = client().get(statisticsPath);
+        assertResponseStatus(200, r);
+    }
+
     @Test
     public void testMetricsSystem() {
         Response r = client().get(path, "system");


Reply via email to