This is an automated email from the ASF dual-hosted git repository.
sk0x50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new dd616ccbe04 IGNITE-25639 Enrich JVM and Os metrics (#6049)
dd616ccbe04 is described below
commit dd616ccbe040973a53c9d81a4d8c7a3b6364f206
Author: Slava Koptilin <[email protected]>
AuthorDate: Mon Jun 16 13:10:22 2025 +0300
IGNITE-25639 Enrich JVM and Os metrics (#6049)
---
.../internal/metrics/AtomicDoubleMetric.java | 3 +-
.../internal/metrics/sources/JvmMetricSource.java | 107 ++++++++--------
.../internal/metrics/sources/OsMetricSource.java | 57 ++++++---
.../metrics/sources/JvmMetricSourceTest.java | 138 +++++++++++++++++++--
4 files changed, 222 insertions(+), 83 deletions(-)
diff --git
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
index 0ff0aeda89a..b5c1ceb36a1 100644
---
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
+++
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.metrics;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import org.jetbrains.annotations.Nullable;
/**
* Double metric based on atomic updater of double value.
@@ -36,7 +37,7 @@ public class AtomicDoubleMetric extends AbstractMetric
implements DoubleMetric {
* @param name Name.
* @param desc Description.
*/
- public AtomicDoubleMetric(String name, String desc) {
+ public AtomicDoubleMetric(String name, @Nullable String desc) {
super(name, desc);
}
diff --git
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/JvmMetricSource.java
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/JvmMetricSource.java
index d0cbb5f1e1f..9b13deba47f 100644
---
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/JvmMetricSource.java
+++
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/JvmMetricSource.java
@@ -21,12 +21,11 @@ import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
-import java.util.HashMap;
+import java.lang.management.RuntimeMXBean;
import java.util.List;
import java.util.function.Supplier;
-import org.apache.ignite.internal.metrics.LongGauge;
-import org.apache.ignite.internal.metrics.Metric;
import org.apache.ignite.internal.metrics.MetricSet;
+import org.apache.ignite.internal.metrics.MetricSetBuilder;
import org.apache.ignite.internal.metrics.MetricSource;
import org.jetbrains.annotations.Nullable;
@@ -45,16 +44,21 @@ public class JvmMetricSource implements MetricSource {
private final List<GarbageCollectorMXBean> gcMxBeans;
+ /** The managed bean for the runtime system of the Java virtual machine. */
+ private final RuntimeMXBean runtimeBean;
+
/** Enablement status. Accessed from different threads under
synchronization on this object. */
private boolean enabled;
/**
* Constructor.
*
+ * @param runtimeBean MXBean implementation to receive runtime info.
* @param memoryMxBean MXBean implementation to receive memory info.
* @param gcMxBeans MXBean implementation to receive GC info.
*/
- JvmMetricSource(MemoryMXBean memoryMxBean, List<GarbageCollectorMXBean>
gcMxBeans) {
+ JvmMetricSource(RuntimeMXBean runtimeBean, MemoryMXBean memoryMxBean,
List<GarbageCollectorMXBean> gcMxBeans) {
+ this.runtimeBean = runtimeBean;
this.memoryMxBean = memoryMxBean;
this.gcMxBeans = List.copyOf(gcMxBeans);
}
@@ -65,6 +69,7 @@ public class JvmMetricSource implements MetricSource {
public JvmMetricSource() {
memoryMxBean = ManagementFactory.getMemoryMXBean();
gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
+ runtimeBean = ManagementFactory.getRuntimeMXBean();
}
@Override
@@ -78,65 +83,57 @@ public class JvmMetricSource implements MetricSource {
return null;
}
- var metrics = new HashMap<String, Metric>();
+ var metricSetBuilder = new MetricSetBuilder(SOURCE_NAME);
CachedMemoryUsage heapMemoryUsage = new
CachedMemoryUsage(memoryMxBean::getHeapMemoryUsage, MEMORY_USAGE_CACHE_TIMEOUT);
- metrics.put("memory.heap.Init",
- new LongGauge(
- "memory.heap.Init",
- "Initial amount of heap memory",
- () -> heapMemoryUsage.get().getInit()
- ));
- metrics.put("memory.heap.Used",
- new LongGauge("memory.heap.Used",
- "Current used amount of heap memory",
- () -> heapMemoryUsage.get().getUsed()
- ));
- metrics.put("memory.heap.Committed",
- new LongGauge("memory.heap.Committed",
- "Committed amount of heap memory",
- () -> heapMemoryUsage.get().getCommitted()
- ));
- metrics.put("memory.heap.Max",
- new LongGauge("memory.heap.Max",
- "Maximum amount of heap memory",
- () -> heapMemoryUsage.get().getMax()
- ));
+ metricSetBuilder.longGauge(
+ "memory.heap.Init",
+ "Initial amount of heap memory",
+ () -> heapMemoryUsage.get().getInit());
+ metricSetBuilder.longGauge(
+ "memory.heap.Used",
+ "Current used amount of heap memory",
+ () -> heapMemoryUsage.get().getUsed());
+ metricSetBuilder.longGauge(
+ "memory.heap.Committed",
+ "Committed amount of heap memory",
+ () -> heapMemoryUsage.get().getCommitted());
+ metricSetBuilder.longGauge(
+ "memory.heap.Max",
+ "Maximum amount of heap memory",
+ () -> heapMemoryUsage.get().getMax());
CachedMemoryUsage nonHeapMemoryUsage = new
CachedMemoryUsage(memoryMxBean::getNonHeapMemoryUsage,
MEMORY_USAGE_CACHE_TIMEOUT);
- metrics.put("memory.non-heap.Init",
- new LongGauge("memory.non-heap.Init",
- "Initial amount of non-heap memory",
- () -> nonHeapMemoryUsage.get().getInit()
- ));
- metrics.put("memory.non-heap.Used",
- new LongGauge("memory.non-heap.Used",
- "Used amount of non-heap memory",
- () -> nonHeapMemoryUsage.get().getUsed()
- ));
- metrics.put("memory.non-heap.Committed",
- new LongGauge("memory.non-heap.Committed",
- "Committed amount of non-heap memory",
- () -> nonHeapMemoryUsage.get().getCommitted()
- ));
- metrics.put("memory.non-heap.Max",
- new LongGauge("memory.non-heap.Max",
- "Maximum amount of non-heap memory",
- () -> nonHeapMemoryUsage.get().getMax()
- ));
-
- metrics.put(
+ metricSetBuilder.longGauge(
+ "memory.non-heap.Init",
+ "Initial amount of non-heap memory",
+ () -> nonHeapMemoryUsage.get().getInit());
+ metricSetBuilder.longGauge(
+ "memory.non-heap.Used",
+ "Used amount of non-heap memory",
+ () -> nonHeapMemoryUsage.get().getUsed());
+ metricSetBuilder.longGauge(
+ "memory.non-heap.Committed",
+ "Committed amount of non-heap memory",
+ () -> nonHeapMemoryUsage.get().getCommitted());
+ metricSetBuilder.longGauge(
+ "memory.non-heap.Max",
+ "Maximum amount of non-heap memory",
+ () -> nonHeapMemoryUsage.get().getMax());
+
+ metricSetBuilder.longGauge(
"gc.CollectionTime",
- new LongGauge(
- "gc.CollectionTime",
- "Approximate total time spent on garbage collection in
milliseconds, summed across all collectors.",
- this::totalCollectionTime
- )
- );
+ "Approximate total time spent on garbage collection in
milliseconds, summed across all collectors.",
+ this::totalCollectionTime);
+
+ metricSetBuilder.longGauge(
+ "UpTime",
+ "The uptime of the Java virtual machine in milliseconds.",
+ runtimeBean::getUptime);
enabled = true;
- return new MetricSet(SOURCE_NAME, metrics);
+ return metricSetBuilder.build();
}
private long totalCollectionTime() {
diff --git
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/OsMetricSource.java
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/OsMetricSource.java
index e8b1aac7aab..d0ca63d8ea3 100644
---
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/OsMetricSource.java
+++
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/sources/OsMetricSource.java
@@ -19,10 +19,11 @@ package org.apache.ignite.internal.metrics.sources;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
-import java.util.HashMap;
-import org.apache.ignite.internal.metrics.DoubleGauge;
-import org.apache.ignite.internal.metrics.Metric;
+import java.util.function.DoubleSupplier;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.metrics.MetricSet;
+import org.apache.ignite.internal.metrics.MetricSetBuilder;
import org.apache.ignite.internal.metrics.MetricSource;
import org.jetbrains.annotations.Nullable;
@@ -30,6 +31,8 @@ import org.jetbrains.annotations.Nullable;
* Metric source which provides OS metrics like Load Average.
*/
public class OsMetricSource implements MetricSource {
+ private final IgniteLogger log = Loggers.forClass(OsMetricSource.class);
+
private static final String SOURCE_NAME = "os";
private final OperatingSystemMXBean operatingSystemMxBean;
@@ -50,7 +53,7 @@ public class OsMetricSource implements MetricSource {
* Constructs new metric source with standard MemoryMXBean as metric
provider.
*/
public OsMetricSource() {
- operatingSystemMxBean = ManagementFactory.getOperatingSystemMXBean();
+ this(ManagementFactory.getOperatingSystemMXBean());
}
@Override
@@ -64,24 +67,29 @@ public class OsMetricSource implements MetricSource {
return null;
}
- var metrics = new HashMap<String, Metric>();
+ var metricSetBuilder = new MetricSetBuilder(SOURCE_NAME);
- metrics.put(
+ metricSetBuilder.doubleGauge(
"LoadAverage",
- new DoubleGauge(
- "LoadAverage",
- "System load average for the last minute. System load
average is the sum of the number of runnable entities "
- + "queued to the available processors and the
number of runnable entities running on the available "
- + "processors averaged over a period of time.
The way in which the load average is calculated depends on "
- + "the operating system. "
- + "If the load average is not available, a
negative value is returned.",
- operatingSystemMxBean::getSystemLoadAverage
- )
+ "System load average for the last minute. System load average
is the sum of the number of runnable entities "
+ + "queued to the available processors and the number
of runnable entities running on the available "
+ + "processors averaged over a period of time. The way
in which the load average is calculated depends on "
+ + "the operating system. "
+ + "If the load average is not available, a negative
value is returned.",
+ operatingSystemMxBean::getSystemLoadAverage
+ );
+
+ metricSetBuilder.doubleGauge(
+ "CpuLoad",
+ "CPU load. The value is between 0.0 and 1.0, where 0.0 means
no CPU load and 1.0 means "
+ + "100% CPU load."
+ + "If the CPU load is not available, a negative value
is returned.",
+ cpuLoadSupplier()
);
enabled = true;
- return new MetricSet(SOURCE_NAME, metrics);
+ return metricSetBuilder.build();
}
@Override
@@ -93,4 +101,21 @@ public class OsMetricSource implements MetricSource {
public synchronized boolean enabled() {
return enabled;
}
+
+ private DoubleSupplier cpuLoadSupplier() {
+ try {
+ if (operatingSystemMxBean instanceof
com.sun.management.OperatingSystemMXBean) {
+ com.sun.management.OperatingSystemMXBean sunOs =
(com.sun.management.OperatingSystemMXBean) operatingSystemMxBean;
+ return sunOs::getProcessCpuLoad;
+ }
+ } catch (NoClassDefFoundError ignored) {
+ // This exception is thrown if the
com.sun.management.OperatingSystemMXBean class is not available.
+ // In this case, we return a supplier that always returns -1.
+ }
+
+ log.warn("The 'com.sun.management.OperatingSystemMXBean' class is not
available for class loader. "
+ + "CPU metrics are not available.");
+
+ return () -> -1.0;
+ }
}
diff --git
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/sources/JvmMetricSourceTest.java
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/sources/JvmMetricSourceTest.java
index 071c7fc7030..06ad1c8a463 100644
---
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/sources/JvmMetricSourceTest.java
+++
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/sources/JvmMetricSourceTest.java
@@ -24,7 +24,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
+import java.lang.management.RuntimeMXBean;
import java.util.List;
+import java.util.Map;
import javax.management.ObjectName;
import org.apache.ignite.internal.metrics.LongMetric;
import org.junit.jupiter.api.Test;
@@ -36,7 +38,8 @@ public class JvmMetricSourceTest {
var memoryBean = new MemoryBean(5, 15, 20, 90,
100, 115, 120, 200);
var gcBean = new GarbageCollectorBean(10, 100);
- var metricSource = new JvmMetricSource(memoryBean, List.of(gcBean));
+ var runtimeBean = new RuntimeBean(1000);
+ var metricSource = new JvmMetricSource(runtimeBean, memoryBean,
List.of(gcBean));
var metricSet = metricSource.enable();
@@ -50,6 +53,8 @@ public class JvmMetricSourceTest {
assertEquals(memoryBean.nonHeapCommitted,
metricSet.<LongMetric>get("memory.non-heap.Committed").value());
assertEquals(memoryBean.nonHeapMax,
metricSet.<LongMetric>get("memory.non-heap.Max").value());
+ assertEquals(memoryBean.nonHeapMax,
metricSet.<LongMetric>get("memory.non-heap.Max").value());
+
memoryBean.heapUsed += 1;
memoryBean.heapCommitted += 1;
@@ -81,7 +86,8 @@ public class JvmMetricSourceTest {
100, 115, 120, 200);
var gcBean1 = new GarbageCollectorBean(10, 100);
var gcBean2 = new GarbageCollectorBean(20, 200);
- var metricSource = new JvmMetricSource(memoryBean, List.of(gcBean1,
gcBean2));
+ var runtimeBean = new RuntimeBean(1000);
+ var metricSource = new JvmMetricSource(runtimeBean, memoryBean,
List.of(gcBean1, gcBean2));
var metricSet = metricSource.enable();
@@ -93,21 +99,38 @@ public class JvmMetricSourceTest {
assertEquals(325,
metricSet.<LongMetric>get("gc.CollectionTime").value());
}
+ @Test
+ public void testUptimeMetric() {
+ var memoryBean = new MemoryBean(5, 15, 20, 90,
+ 100, 115, 120, 200);
+ var gcBean = new GarbageCollectorBean(10, 100);
+ var runtimeBean = new RuntimeBean(1000);
+ var metricSource = new JvmMetricSource(runtimeBean, memoryBean,
List.of(gcBean));
+
+ var metricSet = metricSource.enable();
+
+ assertEquals(runtimeBean.upTime,
metricSet.<LongMetric>get("UpTime").value());
+
+ runtimeBean.upTime += 1000; // Simulate JVM uptime increase
+
+ assertEquals(runtimeBean.upTime,
metricSet.<LongMetric>get("UpTime").value());
+ }
+
/**
* Test implementation of {@link java.lang.management.MemoryMXBean},
* which open for mutations in scope of the current test.
*
*/
- private class MemoryBean implements MemoryMXBean {
- public long heapInit;
- public long heapUsed;
- public long heapCommitted;
- public long heapMax;
+ private static class MemoryBean implements MemoryMXBean {
+ long heapInit;
+ long heapUsed;
+ long heapCommitted;
+ long heapMax;
- public long nonHeapInit;
- public long nonHeapUsed;
- public long nonHeapCommitted;
- public long nonHeapMax;
+ long nonHeapInit;
+ long nonHeapUsed;
+ long nonHeapCommitted;
+ long nonHeapMax;
private MemoryBean(long heapInit, long heapUsed, long heapCommitted,
long heapMax,
long nonHeapInit, long nonHeapUsed, long nonHeapCommitted,
long nonHeapMax) {
@@ -202,4 +225,97 @@ public class JvmMetricSourceTest {
collectionTime += timeDelta;
}
}
+
+ private static class RuntimeBean implements RuntimeMXBean {
+ long upTime;
+
+ RuntimeBean(long upTime) {
+ this.upTime = upTime;
+ }
+
+ @Override
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getVmName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getVmVendor() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getVmVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getSpecName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getSpecVendor() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getSpecVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getManagementSpecVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getClassPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getLibraryPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isBootClassPathSupported() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getBootClassPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<String> getInputArguments() {
+ return List.of();
+ }
+
+ @Override
+ public long getUptime() {
+ return upTime;
+ }
+
+ @Override
+ public long getStartTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, String> getSystemProperties() {
+ return Map.of();
+ }
+
+ @Override
+ public ObjectName getObjectName() {
+ throw new UnsupportedOperationException();
+ }
+ }
}