diff --git a/core/src/main/java/google/registry/monitoring/whitebox/JvmMetrics.java b/core/src/main/java/google/registry/monitoring/whitebox/JvmMetrics.java new file mode 100644 index 00000000000..750683986be --- /dev/null +++ b/core/src/main/java/google/registry/monitoring/whitebox/JvmMetrics.java @@ -0,0 +1,103 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// 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 google.registry.monitoring.whitebox; + +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.monitoring.metrics.LabelDescriptor; +import com.google.monitoring.metrics.MetricRegistry; +import com.google.monitoring.metrics.MetricRegistryImpl; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; + +/** Exposes JVM metrics. */ +@Singleton +class JvmMetrics { + + private static final ImmutableSet TYPE_LABEL_SET = + ImmutableSet.of(LabelDescriptor.create("type", "Memory type (e.g., heap, non_heap)")); + private final MemoryMXBean memoryMxBean; + + @Inject + JvmMetrics() { + this(ManagementFactory.getMemoryMXBean()); + } + + /** Constructor for testing. */ + JvmMetrics(MemoryMXBean memoryMxBean) { + this.memoryMxBean = memoryMxBean; + } + + /** Registers JVM gauges with the default registry. */ + void register() { + MetricRegistry registry = MetricRegistryImpl.getDefault(); + + registry.newGauge( + "/jvm/memory/used", + "Current memory usage in bytes", + "bytes", + TYPE_LABEL_SET, + (Supplier, Long>>) this::getUsedMemory, + Long.class); + + registry.newGauge( + "/jvm/memory/committed", + "Committed memory in bytes", + "bytes", + TYPE_LABEL_SET, + (Supplier, Long>>) this::getCommittedMemory, + Long.class); + + registry.newGauge( + "/jvm/memory/max", + "Maximum memory in bytes", + "bytes", + TYPE_LABEL_SET, + (Supplier, Long>>) this::getMaxMemory, + Long.class); + } + + ImmutableMap, Long> getUsedMemory() { + MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage(); + MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage(); + + return ImmutableMap.of( + ImmutableList.of("heap"), heapUsage.getUsed(), + ImmutableList.of("non_heap"), nonHeapUsage.getUsed()); + } + + ImmutableMap, Long> getCommittedMemory() { + MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage(); + MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage(); + + return ImmutableMap.of( + ImmutableList.of("heap"), heapUsage.getCommitted(), + ImmutableList.of("non_heap"), nonHeapUsage.getCommitted()); + } + + ImmutableMap, Long> getMaxMemory() { + MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage(); + MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage(); + + return ImmutableMap.of( + ImmutableList.of("heap"), heapUsage.getMax(), + ImmutableList.of("non_heap"), nonHeapUsage.getMax()); + } +} diff --git a/core/src/main/java/google/registry/monitoring/whitebox/StackdriverModule.java b/core/src/main/java/google/registry/monitoring/whitebox/StackdriverModule.java index aa89ec1d560..0396e013e17 100644 --- a/core/src/main/java/google/registry/monitoring/whitebox/StackdriverModule.java +++ b/core/src/main/java/google/registry/monitoring/whitebox/StackdriverModule.java @@ -32,7 +32,7 @@ import jakarta.inject.Singleton; import org.joda.time.Duration; -/** Dagger module for Google Stackdriver service connection objects. */ +/** Dagger module for monitoring and Google Stackdriver service connection objects. */ @Module public final class StackdriverModule { @@ -77,7 +77,11 @@ static MetricWriter provideMetricWriter( @Provides static MetricReporter provideMetricReporter( - MetricWriter metricWriter, @Config("metricsWriteInterval") Duration writeInterval) { + MetricWriter metricWriter, + @Config("metricsWriteInterval") Duration writeInterval, + JvmMetrics jvmMetrics) { + jvmMetrics.register(); + return new MetricReporter( metricWriter, writeInterval.getStandardSeconds(), diff --git a/core/src/test/java/google/registry/monitoring/whitebox/JvmMetricsTest.java b/core/src/test/java/google/registry/monitoring/whitebox/JvmMetricsTest.java new file mode 100644 index 00000000000..1bc4f040ecb --- /dev/null +++ b/core/src/test/java/google/registry/monitoring/whitebox/JvmMetricsTest.java @@ -0,0 +1,96 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// 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 google.registry.monitoring.whitebox; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.monitoring.metrics.MetricRegistry; +import com.google.monitoring.metrics.MetricRegistryImpl; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link JvmMetrics}. */ +class JvmMetricsTests { + + private MetricRegistry registry; + private JvmMetrics jvmMetrics; + private MemoryMXBean mockMemoryMXBean = mock(MemoryMXBean.class); + + private static final MemoryUsage HEAP_USAGE = + new MemoryUsage(/*init*/ 100, /*used*/ 200, /*commited*/ 500, /*max*/ 1000); + private static final MemoryUsage NON_HEAP_USAGE = + new MemoryUsage(/*init*/ 50, /*used*/ 100, /*commited*/ 250, /*max*/ 500); + + @BeforeEach + public void setUp() { + MetricRegistryImpl.getDefault().unregisterAllMetrics(); + registry = MetricRegistryImpl.getDefault(); + + when(mockMemoryMXBean.getHeapMemoryUsage()).thenReturn(HEAP_USAGE); + when(mockMemoryMXBean.getNonHeapMemoryUsage()).thenReturn(NON_HEAP_USAGE); + + jvmMetrics = new JvmMetrics(mockMemoryMXBean); + } + + @Test + public void metricsRegistered() { + jvmMetrics.register(); + + assertThat(registry.getRegisteredMetrics()).hasSize(3); + + for (var metric : registry.getRegisteredMetrics()) { + assertThat(metric).isInstanceOf(com.google.monitoring.metrics.VirtualMetric.class); + } + } + + @Test + public void testGetUsedMemory() { + jvmMetrics.register(); + ImmutableMap, Long> values = jvmMetrics.getUsedMemory(); + + assertThat(values) + .containsExactly( + ImmutableList.of("heap"), 200L, + ImmutableList.of("non_heap"), 100L); + } + + @Test + public void testGetCommittedMemory() { + jvmMetrics.register(); + ImmutableMap, Long> values = jvmMetrics.getCommittedMemory(); + + assertThat(values) + .containsExactly( + ImmutableList.of("heap"), 500L, + ImmutableList.of("non_heap"), 250L); + } + + @Test + public void testGetMaxMemory() { + jvmMetrics.register(); + ImmutableMap, Long> values = jvmMetrics.getMaxMemory(); + + assertThat(values) + .containsExactly( + ImmutableList.of("heap"), 1000L, + ImmutableList.of("non_heap"), 500L); + } +}