From 151beba11b09c118c7519b757ad6e35ffcf8ead5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 12 Mar 2026 13:10:08 +0100 Subject: [PATCH 1/6] SOLR-18159 Add metrics for jvm_system_memory_free_bytes and jvm_system_memory_bytes --- .../SOLR-18159-physical-memory-metrics.yml | 9 ++++ .../solr/metrics/OtelRuntimeJvmMetrics.java | 48 ++++++++++++++++++ .../apache/solr/metrics/JvmMetricsTest.java | 49 +++++++++++++++++++ .../pages/metrics-reporting.adoc | 15 ++++++ 4 files changed, 121 insertions(+) create mode 100644 changelog/unreleased/SOLR-18159-physical-memory-metrics.yml diff --git a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml new file mode 100644 index 000000000000..4e3b847a7c1e --- /dev/null +++ b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml @@ -0,0 +1,9 @@ +title: Add jvm_system_memory_bytes and jvm_system_memory_free_bytes gauges exposing + total and free physical (host or container) memory to the Prometheus metrics endpoint. +type: added +authors: + - name: Jan Høydahl + url: https://home.apache.org/phonebook.html?uid=janhoy +links: + - name: SOLR-18159 + url: https://issues.apache.org/jira/browse/SOLR-18159 diff --git a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java index 19803372dae9..bf58ffaa2ca9 100644 --- a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java +++ b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java @@ -18,11 +18,15 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.metrics.ObservableLongGauge; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.instrumentation.runtimemetrics.java17.RuntimeMetrics; import java.lang.invoke.MethodHandles; +import java.lang.management.ManagementFactory; +import org.apache.lucene.util.SuppressForbidden; import org.apache.solr.common.util.EnvUtils; +import org.apache.solr.metrics.otel.OtelUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +35,8 @@ public class OtelRuntimeJvmMetrics { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private RuntimeMetrics runtimeMetrics; + private ObservableLongGauge systemMemoryTotalGauge; + private ObservableLongGauge systemMemoryFreeGauge; private boolean isInitialized = false; // Main feature flag to enable/disable all JVM metrics @@ -38,6 +44,10 @@ public static boolean isJvmMetricsEnabled() { return EnvUtils.getPropertyAsBool("solr.metrics.jvm.enabled", true); } + @SuppressForbidden( + reason = + "com.sun.management.OperatingSystemMXBean is used intentionally for physical memory" + + " gauges; guarded by instanceof check so gracefully absent on non-HotSpot JVMs") public OtelRuntimeJvmMetrics initialize( SolrMetricManager solrMetricManager, String registryName) { if (!isJvmMetricsEnabled()) return this; @@ -65,6 +75,36 @@ public ContextPropagators getPropagators() { // TODO: We should have this configurable to enable/disable specific JVM metrics .enableAllFeatures() .build(); + java.lang.management.OperatingSystemMXBean osMxBean = + ManagementFactory.getOperatingSystemMXBean(); + if (osMxBean instanceof com.sun.management.OperatingSystemMXBean extOsMxBean) { + systemMemoryTotalGauge = + solrMetricManager.observableLongGauge( + registryName, + "jvm.system.memory", + "Total physical memory of the host or container in bytes." + + " On Linux with cgroup limits, reflects the container memory limit.", + measurement -> { + long total = extOsMxBean.getTotalMemorySize(); + if (total > 0) measurement.record(total); + }, + OtelUnit.BYTES); + systemMemoryFreeGauge = + solrMetricManager.observableLongGauge( + registryName, + "jvm.system.memory.free", + "Free (unused) physical memory of the host or container in bytes.", + measurement -> { + long free = extOsMxBean.getFreeMemorySize(); + if (free > 0) measurement.record(free); + }, + OtelUnit.BYTES); + log.info("Physical memory metrics enabled"); + } else { + log.info( + "Physical memory metrics unavailable:" + + " com.sun.management.OperatingSystemMXBean not present on this JVM"); + } isInitialized = true; log.info("JVM metrics collection successfully initialized"); return this; @@ -74,6 +114,14 @@ public void close() { if (runtimeMetrics != null && isInitialized) { try { runtimeMetrics.close(); + if (systemMemoryTotalGauge != null) { + systemMemoryTotalGauge.close(); + systemMemoryTotalGauge = null; + } + if (systemMemoryFreeGauge != null) { + systemMemoryFreeGauge.close(); + systemMemoryFreeGauge = null; + } } catch (Exception e) { log.error("Failed to close JVM metrics collection", e); } finally { diff --git a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java index b3b9f42cd5d4..8ea22b93be42 100644 --- a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java +++ b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java @@ -24,10 +24,12 @@ import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; +import org.apache.lucene.util.SuppressForbidden; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.core.NodeConfig; import org.apache.solr.core.SolrXmlConfig; import org.apache.solr.util.SolrJettyTestRule; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -105,4 +107,51 @@ public void testSetupJvmMetrics() throws InterruptedException { "Should have JVM buffer metrics", metricNames.stream().anyMatch(name -> name.startsWith("jvm_buffer"))); } + + @Test + @SuppressForbidden(reason = "Testing com.sun.management.OperatingSystemMXBean availability") + public void testSystemMemoryMetrics() { + PrometheusMetricReader reader = + solrTestRule + .getJetty() + .getCoreContainer() + .getMetricManager() + .getPrometheusMetricReader("solr.jvm"); + MetricSnapshots snapshots = reader.collect(); + + Set metricNames = + snapshots.stream() + .map(metric -> metric.getMetadata().getPrometheusName()) + .collect(Collectors.toSet()); + + // Physical memory metrics are only present on HotSpot JDK (com.sun.management available). + // If absent, the test is skipped (graceful degradation is tested separately). + boolean isHotSpot = + java.lang.management.ManagementFactory.getOperatingSystemMXBean() + instanceof com.sun.management.OperatingSystemMXBean; + Assume.assumeTrue( + "Skipping: com.sun.management.OperatingSystemMXBean not available", isHotSpot); + + assertTrue( + "Should have jvm_system_memory_bytes metric", + metricNames.contains("jvm_system_memory_bytes")); + assertTrue( + "Should have jvm_system_memory_free_bytes metric", + metricNames.contains("jvm_system_memory_free_bytes")); + } + + @Test + public void testJvmMetricsDisabledNoSystemMemory() throws Exception { + // Verify that when JVM metrics are disabled, initialization is a no-op and close() is safe + SolrMetricManager metricManager = solrTestRule.getJetty().getCoreContainer().getMetricManager(); + System.setProperty("solr.metrics.jvm.enabled", "false"); + try { + OtelRuntimeJvmMetrics disabledMetrics = new OtelRuntimeJvmMetrics(); + OtelRuntimeJvmMetrics result = disabledMetrics.initialize(metricManager, "solr.jvm"); + assertFalse("Should not be initialized when JVM metrics disabled", result.isInitialized()); + disabledMetrics.close(); // must not throw + } finally { + System.setProperty("solr.metrics.jvm.enabled", "true"); + } + } } diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc index 7d0e79a2c0a5..7875f3ca043e 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc @@ -98,6 +98,21 @@ The `JVM Registry` gathers metrics from the JVM using the OpenTelemetry instrume JVM metrics are enabled by default but can be disabled by setting either the system property `-Dsolr.metrics.jvm.enabled=false` or the environment variable `SOLR_METRICS_JVM_ENABLED=false`. +==== Physical Memory Metrics + +Solr exposes two additional gauges for host or container physical memory, registered under the `solr.jvm` registry: + +[cols="2,1,3",options="header"] +|=== +| Prometheus Metric Name | Type | Description +| `jvm_system_memory_bytes` | gauge | Total physical memory of the host or container in bytes. On Linux with cgroup memory limits, reflects the container limit rather than host RAM. +| `jvm_system_memory_free_bytes` | gauge | Free (unused) physical memory of the host or container in bytes. +|=== + +NOTE: These metrics are available only on HotSpot-based JDK distributions (Oracle JDK, Eclipse Temurin, Amazon Corretto, IBM Semeru, and others). On JVMs that do not provide `com.sun.management.OperatingSystemMXBean`, the metrics are silently absent. + +NOTE: On Linux containers with cgroup v1 or v2 memory limits set, the JDK reports the container memory limit as the total, not the host's physical RAM. This is the correct value for calculating MMap cache efficiency in containerised deployments. + === Overseer Registry The `Overseer Registry` is initialized when running in SolrCloud mode and includes the following information: From 50599e0c02b5d49c9ea88e2114b22d38736f2157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Thu, 12 Mar 2026 15:46:40 +0100 Subject: [PATCH 2/6] SOLR-18159 Fix ECJ lint, review feedback on physical memory metrics - Add imports for ManagementFactory and com.sun.management.OperatingSystemMXBean in JvmMetricsTest to fix UnnecessarilyFullyQualified ECJ lint errors - Restore system property correctly in test finally block instead of hardcoding "true" - Update comment to remove inaccurate claim about separate graceful-degradation test - Downgrade "OperatingSystemMXBean not present" log from INFO to DEBUG - Update ref-guide NOTE to describe API availability without listing JDK vendors --- .../solr/metrics/OtelRuntimeJvmMetrics.java | 8 +++++--- .../org/apache/solr/metrics/JvmMetricsTest.java | 16 +++++++++++----- .../pages/metrics-reporting.adoc | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java index bf58ffaa2ca9..1c5af2af68ed 100644 --- a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java +++ b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java @@ -101,9 +101,11 @@ public ContextPropagators getPropagators() { OtelUnit.BYTES); log.info("Physical memory metrics enabled"); } else { - log.info( - "Physical memory metrics unavailable:" - + " com.sun.management.OperatingSystemMXBean not present on this JVM"); + if (log.isDebugEnabled()) { + log.debug( + "Physical memory metrics unavailable:" + + " com.sun.management.OperatingSystemMXBean not present on this JVM"); + } } isInitialized = true; log.info("JVM metrics collection successfully initialized"); diff --git a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java index 8ea22b93be42..44d6558c93a3 100644 --- a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java +++ b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java @@ -16,8 +16,10 @@ */ package org.apache.solr.metrics; +import com.sun.management.OperatingSystemMXBean; import io.opentelemetry.exporter.prometheus.PrometheusMetricReader; import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import java.lang.management.ManagementFactory; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -124,11 +126,10 @@ public void testSystemMemoryMetrics() { .map(metric -> metric.getMetadata().getPrometheusName()) .collect(Collectors.toSet()); - // Physical memory metrics are only present on HotSpot JDK (com.sun.management available). - // If absent, the test is skipped (graceful degradation is tested separately). + // Physical memory metrics are only present when com.sun.management.OperatingSystemMXBean + // is available. If absent, the test is skipped. boolean isHotSpot = - java.lang.management.ManagementFactory.getOperatingSystemMXBean() - instanceof com.sun.management.OperatingSystemMXBean; + ManagementFactory.getOperatingSystemMXBean() instanceof OperatingSystemMXBean; Assume.assumeTrue( "Skipping: com.sun.management.OperatingSystemMXBean not available", isHotSpot); @@ -144,6 +145,7 @@ public void testSystemMemoryMetrics() { public void testJvmMetricsDisabledNoSystemMemory() throws Exception { // Verify that when JVM metrics are disabled, initialization is a no-op and close() is safe SolrMetricManager metricManager = solrTestRule.getJetty().getCoreContainer().getMetricManager(); + String prevValue = System.getProperty("solr.metrics.jvm.enabled"); System.setProperty("solr.metrics.jvm.enabled", "false"); try { OtelRuntimeJvmMetrics disabledMetrics = new OtelRuntimeJvmMetrics(); @@ -151,7 +153,11 @@ public void testJvmMetricsDisabledNoSystemMemory() throws Exception { assertFalse("Should not be initialized when JVM metrics disabled", result.isInitialized()); disabledMetrics.close(); // must not throw } finally { - System.setProperty("solr.metrics.jvm.enabled", "true"); + if (prevValue == null) { + System.clearProperty("solr.metrics.jvm.enabled"); + } else { + System.setProperty("solr.metrics.jvm.enabled", prevValue); + } } } } diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc index 7875f3ca043e..945aab7c8d3a 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc @@ -109,7 +109,7 @@ Solr exposes two additional gauges for host or container physical memory, regist | `jvm_system_memory_free_bytes` | gauge | Free (unused) physical memory of the host or container in bytes. |=== -NOTE: These metrics are available only on HotSpot-based JDK distributions (Oracle JDK, Eclipse Temurin, Amazon Corretto, IBM Semeru, and others). On JVMs that do not provide `com.sun.management.OperatingSystemMXBean`, the metrics are silently absent. +NOTE: These metrics are available when the JVM provides the `com.sun.management.OperatingSystemMXBean` interface (this includes most HotSpot-derived JVMs). On JVMs that do not provide `com.sun.management.OperatingSystemMXBean`, the metrics are silently absent. NOTE: On Linux containers with cgroup v1 or v2 memory limits set, the JDK reports the container memory limit as the total, not the host's physical RAM. This is the correct value for calculating MMap cache efficiency in containerised deployments. From 5a25f12c73a0b09b803df681b74803a6d176b4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 13 Mar 2026 00:04:16 +0100 Subject: [PATCH 3/6] Shorten changelog after feedback --- changelog/unreleased/SOLR-18159-physical-memory-metrics.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml index 4e3b847a7c1e..69fbcee8b4d8 100644 --- a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml +++ b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml @@ -1,5 +1,4 @@ -title: Add jvm_system_memory_bytes and jvm_system_memory_free_bytes gauges exposing - total and free physical (host or container) memory to the Prometheus metrics endpoint. +title: Add jvm.system.memory.bytes and jvm.system.memory.free.bytes metric gauges type: added authors: - name: Jan Høydahl From a74cbb3589b8c2ddd612f045d3867eda27d0d520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Fri, 13 Mar 2026 00:58:15 +0100 Subject: [PATCH 4/6] SOLR-18159 Merge system memory gauges into one with state=total|free attribute - Replace separate jvm.system.memory and jvm.system.memory.free gauges with a single jvm.system.memory gauge using state attribute (total/free), producing jvm_system_memory_bytes{state="total"} and {state="free"} - Use >= 0 guard (instead of > 0) since the API returns -1 when unavailable - Update test, ref-guide, and changelog accordingly Co-Authored-By: Claude Sonnet 4.6 --- .../SOLR-18159-physical-memory-metrics.yml | 2 +- .../solr/metrics/OtelRuntimeJvmMetrics.java | 34 +++++++------------ .../apache/solr/metrics/JvmMetricsTest.java | 5 +-- .../pages/metrics-reporting.adoc | 6 ++-- 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml index 69fbcee8b4d8..be6cef460754 100644 --- a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml +++ b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml @@ -1,4 +1,4 @@ -title: Add jvm.system.memory.bytes and jvm.system.memory.free.bytes metric gauges +title: Add new metric jvm_system_memory_bytes type: added authors: - name: Jan Høydahl diff --git a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java index 1c5af2af68ed..375d4df8ae34 100644 --- a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java +++ b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java @@ -17,6 +17,8 @@ package org.apache.solr.metrics; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.metrics.ObservableLongGauge; import io.opentelemetry.api.trace.TracerProvider; @@ -33,10 +35,10 @@ /** Manages JVM metrics collection using OpenTelemetry Runtime Metrics with JFR features */ public class OtelRuntimeJvmMetrics { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final AttributeKey STATE_KEY = AttributeKey.stringKey("state"); private RuntimeMetrics runtimeMetrics; - private ObservableLongGauge systemMemoryTotalGauge; - private ObservableLongGauge systemMemoryFreeGauge; + private ObservableLongGauge systemMemoryGauge; private boolean isInitialized = false; // Main feature flag to enable/disable all JVM metrics @@ -78,25 +80,17 @@ public ContextPropagators getPropagators() { java.lang.management.OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean(); if (osMxBean instanceof com.sun.management.OperatingSystemMXBean extOsMxBean) { - systemMemoryTotalGauge = + systemMemoryGauge = solrMetricManager.observableLongGauge( registryName, "jvm.system.memory", - "Total physical memory of the host or container in bytes." - + " On Linux with cgroup limits, reflects the container memory limit.", + "Physical memory of the host or container in bytes (state=total|free)." + + " On Linux with cgroup limits, total reflects the container memory limit.", measurement -> { long total = extOsMxBean.getTotalMemorySize(); - if (total > 0) measurement.record(total); - }, - OtelUnit.BYTES); - systemMemoryFreeGauge = - solrMetricManager.observableLongGauge( - registryName, - "jvm.system.memory.free", - "Free (unused) physical memory of the host or container in bytes.", - measurement -> { long free = extOsMxBean.getFreeMemorySize(); - if (free > 0) measurement.record(free); + if (total >= 0) measurement.record(total, Attributes.of(STATE_KEY, "total")); + if (free >= 0) measurement.record(free, Attributes.of(STATE_KEY, "free")); }, OtelUnit.BYTES); log.info("Physical memory metrics enabled"); @@ -116,13 +110,9 @@ public void close() { if (runtimeMetrics != null && isInitialized) { try { runtimeMetrics.close(); - if (systemMemoryTotalGauge != null) { - systemMemoryTotalGauge.close(); - systemMemoryTotalGauge = null; - } - if (systemMemoryFreeGauge != null) { - systemMemoryFreeGauge.close(); - systemMemoryFreeGauge = null; + if (systemMemoryGauge != null) { + systemMemoryGauge.close(); + systemMemoryGauge = null; } } catch (Exception e) { log.error("Failed to close JVM metrics collection", e); diff --git a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java index 44d6558c93a3..ca2a8f4b0078 100644 --- a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java +++ b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java @@ -134,11 +134,8 @@ public void testSystemMemoryMetrics() { "Skipping: com.sun.management.OperatingSystemMXBean not available", isHotSpot); assertTrue( - "Should have jvm_system_memory_bytes metric", + "Should have jvm_system_memory_bytes metric (with state=total and state=free)", metricNames.contains("jvm_system_memory_bytes")); - assertTrue( - "Should have jvm_system_memory_free_bytes metric", - metricNames.contains("jvm_system_memory_free_bytes")); } @Test diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc index 945aab7c8d3a..58bf50ebf022 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc @@ -100,13 +100,13 @@ JVM metrics are enabled by default but can be disabled by setting either the sys ==== Physical Memory Metrics -Solr exposes two additional gauges for host or container physical memory, registered under the `solr.jvm` registry: +Solr exposes a gauge for host or container physical memory, registered under the `solr.jvm` registry: [cols="2,1,3",options="header"] |=== | Prometheus Metric Name | Type | Description -| `jvm_system_memory_bytes` | gauge | Total physical memory of the host or container in bytes. On Linux with cgroup memory limits, reflects the container limit rather than host RAM. -| `jvm_system_memory_free_bytes` | gauge | Free (unused) physical memory of the host or container in bytes. +| `jvm_system_memory_bytes{state="total"}` | gauge | Total physical memory of the host or container in bytes. On Linux with cgroup memory limits, reflects the container limit rather than host RAM. +| `jvm_system_memory_bytes{state="free"}` | gauge | Free (unused) physical memory of the host or container in bytes. |=== NOTE: These metrics are available when the JVM provides the `com.sun.management.OperatingSystemMXBean` interface (this includes most HotSpot-derived JVMs). On JVMs that do not provide `com.sun.management.OperatingSystemMXBean`, the metrics are silently absent. From 96cb83eb62db66e994ad8e881a9cdeb8564f69c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Mon, 16 Mar 2026 23:06:58 +0100 Subject: [PATCH 5/6] Review feedback - move constant --- .../org/apache/solr/metrics/OtelRuntimeJvmMetrics.java | 8 ++++---- .../java/org/apache/solr/metrics/SolrMetricProducer.java | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java index 375d4df8ae34..a9c14bab2267 100644 --- a/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java +++ b/solr/core/src/java/org/apache/solr/metrics/OtelRuntimeJvmMetrics.java @@ -16,8 +16,9 @@ */ package org.apache.solr.metrics; +import static org.apache.solr.metrics.SolrMetricProducer.STATE_KEY_ATTR; + import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.metrics.ObservableLongGauge; @@ -35,7 +36,6 @@ /** Manages JVM metrics collection using OpenTelemetry Runtime Metrics with JFR features */ public class OtelRuntimeJvmMetrics { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private static final AttributeKey STATE_KEY = AttributeKey.stringKey("state"); private RuntimeMetrics runtimeMetrics; private ObservableLongGauge systemMemoryGauge; @@ -89,8 +89,8 @@ public ContextPropagators getPropagators() { measurement -> { long total = extOsMxBean.getTotalMemorySize(); long free = extOsMxBean.getFreeMemorySize(); - if (total >= 0) measurement.record(total, Attributes.of(STATE_KEY, "total")); - if (free >= 0) measurement.record(free, Attributes.of(STATE_KEY, "free")); + if (total >= 0) measurement.record(total, Attributes.of(STATE_KEY_ATTR, "total")); + if (free >= 0) measurement.record(free, Attributes.of(STATE_KEY_ATTR, "free")); }, OtelUnit.BYTES); log.info("Physical memory metrics enabled"); diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java index 9631c5bedfd5..d98545b36df7 100644 --- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java +++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java @@ -30,6 +30,7 @@ public interface SolrMetricProducer extends AutoCloseable { public static final AttributeKey RESULT_ATTR = AttributeKey.stringKey("result"); public static final AttributeKey NAME_ATTR = AttributeKey.stringKey("name"); public static final AttributeKey PLUGIN_NAME_ATTR = AttributeKey.stringKey("plugin_name"); + public static final AttributeKey STATE_KEY_ATTR = AttributeKey.stringKey("state"); /** * Unique metric tag identifies components with the same life-cycle, which should be registered / From de267803fb931654b08ee119ac70183b9f908ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Tue, 17 Mar 2026 17:02:45 +0100 Subject: [PATCH 6/6] Add Matthew to changelog --- changelog/unreleased/SOLR-18159-physical-memory-metrics.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml index be6cef460754..c24a17cd3e3b 100644 --- a/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml +++ b/changelog/unreleased/SOLR-18159-physical-memory-metrics.yml @@ -3,6 +3,7 @@ type: added authors: - name: Jan Høydahl url: https://home.apache.org/phonebook.html?uid=janhoy + - name: Matthew Biscocho links: - name: SOLR-18159 url: https://issues.apache.org/jira/browse/SOLR-18159