This is an automated email from the ASF dual-hosted git repository.
yangjiaqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph.git
The following commit(s) were added to refs/heads/master by this push:
new fc9bc2866 feat(api): support metric API Prometheus format & add
statistic metric api (#2286)
fc9bc2866 is described below
commit fc9bc2866e4bbcd65bf67a6dbbeb541b06da8649
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-api/src/main/java/org/apache/hugegraph/api/API.java
b/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
index 99fe67e5b..e57f6739d 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
+++ b/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-api/src/main/java/org/apache/hugegraph/api/filter/AccessLogFilter.java
b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/AccessLogFilter.java
new file mode 100644
index 000000000..ba9c98118
--- /dev/null
+++
b/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-api/src/main/java/org/apache/hugegraph/api/filter/PathFilter.java
b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/PathFilter.java
new file mode 100644
index 000000000..3414d6831
--- /dev/null
+++
b/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-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
b/hugegraph-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
index 6df4f6453..f74286b5f 100644
---
a/hugegraph-api/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java
+++
b/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-api/src/main/java/org/apache/hugegraph/metrics/MetricsKeys.java
b/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsKeys.java
new file mode 100644
index 000000000..1cda15c82
--- /dev/null
+++ b/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-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
b/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
index fabd1df7b..bb411f927 100644
--- a/hugegraph-api/src/main/java/org/apache/hugegraph/metrics/MetricsUtil.java
+++ b/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-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
b/hugegraph-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
index 499103c17..cce5af30c 100644
--- a/hugegraph-test/src/main/java/org/apache/hugegraph/api/MetricsApiTest.java
+++ b/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");