feat(api): support metric API Prometheus format & add statistic metric api (#2286)

This commit is contained in:
SunnyBoy-WYH 2023-10-08 20:21:09 +08:00 committed by GitHub
parent 30ef2f7c6b
commit fc9bc2866e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 645 additions and 39 deletions

View File

@ -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";

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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 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.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;
import org.apache.hugegraph.core.GraphManager;
import org.apache.hugegraph.metrics.MetricsModule;
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 com.codahale.metrics.Metric;
import com.codahale.metrics.annotation.Timed;
@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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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");