Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<optional>true</optional>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.monitor.MetricsMonitor;
import com.alibaba.nacos.common.http.HttpRestResult;
import io.prometheus.client.Histogram;

import java.util.Date;
import java.util.Map;
Expand Down Expand Up @@ -55,14 +54,15 @@ public HttpRestResult<String> httpGet(String path, Map<String, String> headers,
Map<String, String> paramValues,
String encode, long readTimeoutMs) throws Exception {
Date start = new Date();
Histogram.Child histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, DEFAULT_CODE);
MetricsMonitor.MetricsTimer timer = MetricsMonitor.getConfigRequestMonitor(
GET, path, DEFAULT_CODE);
HttpRestResult<String> result;
try {
result = httpAgent.httpGet(path, headers, paramValues, encode, readTimeoutMs);
histogram = MetricsMonitor.getConfigRequestMonitor(GET, path,
timer = MetricsMonitor.getConfigRequestMonitor(GET, path,
String.valueOf(result.getCode()));
} finally {
histogram.observe(System.currentTimeMillis() - start.getTime());
timer.observe(System.currentTimeMillis() - start.getTime());
}

return result;
Expand All @@ -73,14 +73,15 @@ public HttpRestResult<String> httpPost(String path, Map<String, String> headers,
Map<String, String> paramValues,
String encode, long readTimeoutMs) throws Exception {
Date start = new Date();
Histogram.Child histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, DEFAULT_CODE);
MetricsMonitor.MetricsTimer timer = MetricsMonitor.getConfigRequestMonitor(
POST, path, DEFAULT_CODE);
HttpRestResult<String> result;
try {
result = httpAgent.httpPost(path, headers, paramValues, encode, readTimeoutMs);
histogram = MetricsMonitor.getConfigRequestMonitor(GET, path,
timer = MetricsMonitor.getConfigRequestMonitor(POST, path,
String.valueOf(result.getCode()));
} finally {
histogram.observe(System.currentTimeMillis() - start.getTime());
timer.observe(System.currentTimeMillis() - start.getTime());
}

return result;
Expand All @@ -91,14 +92,15 @@ public HttpRestResult<String> httpDelete(String path, Map<String, String> header
Map<String, String> paramValues,
String encode, long readTimeoutMs) throws Exception {
Date start = new Date();
Histogram.Child histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, DEFAULT_CODE);
MetricsMonitor.MetricsTimer timer = MetricsMonitor.getConfigRequestMonitor(
DELETE, path, DEFAULT_CODE);
HttpRestResult<String> result;
try {
result = httpAgent.httpDelete(path, headers, paramValues, encode, readTimeoutMs);
histogram = MetricsMonitor.getConfigRequestMonitor(GET, path,
timer = MetricsMonitor.getConfigRequestMonitor(DELETE, path,
String.valueOf(result.getCode()));
} finally {
histogram.observe(System.currentTimeMillis() - start.getTime());
timer.observe(System.currentTimeMillis() - start.getTime());
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ void removeCache(String dataId, String group, String tenant) {

if (enableClientMetrics) {
try {
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size());
MetricsMonitor.recordListenConfigCount(cacheMap.get().size());
} catch (Throwable t) {
LOGGER.error("Failed to update metrics for listen config count", t);
}
Expand Down Expand Up @@ -421,7 +421,7 @@ public CacheData addCacheDataIfAbsent(String dataId, String group) {

if (enableClientMetrics) {
try {
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size());
MetricsMonitor.recordListenConfigCount(cacheMap.get().size());
} catch (Throwable t) {
LOGGER.error("Failed to update metrics for listen config count", t);
}
Expand Down Expand Up @@ -477,7 +477,7 @@ public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant

if (enableClientMetrics) {
try {
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size());
MetricsMonitor.recordListenConfigCount(cacheMap.get().size());
} catch (Throwable t) {
LOGGER.error("Failed to update metrics for listen config count", t);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,142 @@

package com.alibaba.nacos.client.monitor;

import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;

/**
* Metrics Monitor.
*
* <p>Prometheus dependency is optional. If prometheus client is not on the
* classpath, all monitoring operations become no-ops.
*
* @author Nacos
*/
public class MetricsMonitor {

private static final Gauge NACOS_MONITOR_GAUGE =
Gauge.build().name("nacos_monitor").labelNames("module", "name")
.help("nacos_monitor").register();
private static final boolean PROMETHEUS_AVAILABLE;

static {
boolean available;
try {
Class.forName("io.prometheus.client.Counter");
available = true;
} catch (ClassNotFoundException e) {
available = false;
}
PROMETHEUS_AVAILABLE = available;
}

private static volatile Object gauge;
private static volatile Object histogram;
private static volatile Object counter;

private static final Histogram NACOS_CLIENT_REQUEST_HISTOGRAM = Histogram.build()
.labelNames("module", "method", "url", "code").name("nacos_client_request")
.help("nacos_client_request")
.register();
private static Object getOrInitGauge() {
if (gauge == null) {
synchronized (MetricsMonitor.class) {
if (gauge == null) {
gauge = PrometheusMetricsHelper.createGauge("nacos_monitor",
"nacos_monitor", "module", "name");
}
}
}
return gauge;
}

private static final Counter NACOS_CLIENT_NAMING_REQUEST_FAILED_TOTAL = Counter.build()
.name("nacos_client_naming_request_failed_total")
.help("nacos_client_naming_request_failed_total")
.labelNames("module", "req_class", "res_status", "res_code", "err_class").register();
private static Object getOrInitHistogram() {
if (histogram == null) {
synchronized (MetricsMonitor.class) {
if (histogram == null) {
histogram = PrometheusMetricsHelper.createHistogram(
"nacos_client_request", "nacos_client_request",
"module", "method", "url", "code");
}
}
}
return histogram;
}

private static Object getOrInitCounter() {
if (counter == null) {
synchronized (MetricsMonitor.class) {
if (counter == null) {
counter = PrometheusMetricsHelper.createCounter(
"nacos_client_naming_request_failed_total",
"nacos_client_naming_request_failed_total",
"module", "req_class", "res_status", "res_code",
"err_class");
}
}
}
return counter;
}

public static MetricsTimer getConfigRequestMonitor(String method, String url,
String code) {
if (!PROMETHEUS_AVAILABLE) {
return MetricsTimer.NOOP;
}
return PrometheusMetricsHelper.getHistogramChild(getOrInitHistogram(),
"config", method, url, code);
}

public static Gauge.Child getServiceInfoMapSizeMonitor() {
return NACOS_MONITOR_GAUGE.labels("naming", "serviceInfoMapSize");
public static MetricsTimer getNamingRequestMonitor(String method, String url,
String code) {
if (!PROMETHEUS_AVAILABLE) {
return MetricsTimer.NOOP;
}
return PrometheusMetricsHelper.getHistogramChild(getOrInitHistogram(),
"naming", method, url, code);
}

public static Gauge.Child getListenConfigCountMonitor() {
return NACOS_MONITOR_GAUGE.labels("config", "listenConfigCount");
/**
* Record the size of service info map.
*
* @param size the size of service info map
*/
public static void recordServiceInfoMapSize(double size) {
if (!PROMETHEUS_AVAILABLE) {
return;
}
PrometheusMetricsHelper.setGaugeChild(getOrInitGauge(), size,
"naming", "serviceInfoMapSize");
}

public static Histogram.Child getConfigRequestMonitor(String method, String url, String code) {
return NACOS_CLIENT_REQUEST_HISTOGRAM.labels("config", method, url, code);
/**
* Record the count of listened configs.
*
* @param count the count of listened configs
*/
public static void recordListenConfigCount(double count) {
if (!PROMETHEUS_AVAILABLE) {
return;
}
PrometheusMetricsHelper.setGaugeChild(getOrInitGauge(), count,
"config", "listenConfigCount");
}

public static Histogram.Child getNamingRequestMonitor(String method, String url, String code) {
return NACOS_CLIENT_REQUEST_HISTOGRAM.labels("naming", method, url, code);
/**
* Record a failed naming request.
*
* @param reqClass the request class name
* @param resStatus the response status
* @param resCode the response code
* @param errClass the error class name
*/
public static void recordNamingRequestFailed(String reqClass, String resStatus,
String resCode, String errClass) {
if (!PROMETHEUS_AVAILABLE) {
return;
}
PrometheusMetricsHelper.incCounterChild(getOrInitCounter(),
"naming", reqClass, resStatus, resCode, errClass);
}

public static Counter.Child getNamingRequestFailedMonitor(String reqClass, String resStatus,
String resCode,
String errClass) {
return NACOS_CLIENT_NAMING_REQUEST_FAILED_TOTAL.labels("naming", reqClass, resStatus,
resCode, errClass);
/**
* Timer abstraction that wraps prometheus Histogram.Child observation.
*/
public interface MetricsTimer {

MetricsTimer NOOP = duration -> {
};

void observe(double durationMs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 1999-2026 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.client.monitor;

import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;

/**
* Prometheus metrics helper. This class is only loaded when prometheus client
* is on the classpath. It isolates all direct prometheus API calls so that
* {@link MetricsMonitor} can guard against ClassNotFoundException.
*
* @author Nacos
*/
final class PrometheusMetricsHelper {

private PrometheusMetricsHelper() {
}

static Object createGauge(String name, String help, String... labelNames) {
return Gauge.build().name(name).labelNames(labelNames).help(help).register();
}

static Object createHistogram(String name, String help, String... labelNames) {
return Histogram.build().name(name).labelNames(labelNames).help(help).register();
}

static Object createCounter(String name, String help, String... labelNames) {
return Counter.build().name(name).labelNames(labelNames).help(help).register();
}

static MetricsMonitor.MetricsTimer getHistogramChild(Object histogramObj,
String... labelValues) {
Histogram histogram = (Histogram) histogramObj;
Histogram.Child child = histogram.labels(labelValues);
return child::observe;
}

static void setGaugeChild(Object gaugeObj, double value, String... labelValues) {
Gauge gauge = (Gauge) gaugeObj;
gauge.labels(labelValues).set(value);
}

static void incCounterChild(Object counterObj, String... labelValues) {
Counter counter = (Counter) counterObj;
counter.labels(labelValues).inc();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {

if (enableClientMetrics) {
try {
MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());
MetricsMonitor.recordServiceInfoMapSize(serviceInfoMap.size());
} catch (Throwable t) {
NAMING_LOGGER.error("Failed to update metrics for service info map size", t);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,14 +562,14 @@ private void recordRequestFailedMetrics(Request request, Exception exception,

try {
if (Objects.isNull(response)) {
MetricsMonitor.getNamingRequestFailedMonitor(request.getClass().getSimpleName(),
MetricsMonitor.recordNamingRequestFailed(request.getClass().getSimpleName(),
MONITOR_LABEL_NONE,
MONITOR_LABEL_NONE, exception.getClass().getSimpleName()).inc();
MONITOR_LABEL_NONE, exception.getClass().getSimpleName());
} else {
MetricsMonitor.getNamingRequestFailedMonitor(request.getClass().getSimpleName(),
MetricsMonitor.recordNamingRequestFailed(request.getClass().getSimpleName(),
String.valueOf(response.getResultCode()),
String.valueOf(response.getErrorCode()),
MONITOR_LABEL_NONE).inc();
MONITOR_LABEL_NONE);
}
} catch (Throwable t) {
NAMING_LOGGER.warn("Fail to record metrics for request {}",
Expand Down
Loading
Loading