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

Reply via email to