Skip to content

Commit

Permalink
Add JVM classes instrumentation (open-telemetry#6069)
Browse files Browse the repository at this point in the history
* Add JVM classes instrumentation

* Add unit of 1

* Assert monotonicity
  • Loading branch information
jack-berg authored and RashmiRam committed May 23, 2022
1 parent f65f7fd commit fcf5fb5
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.google.auto.service.AutoService;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.instrumentation.runtimemetrics.Classes;
import io.opentelemetry.instrumentation.runtimemetrics.GarbageCollector;
import io.opentelemetry.instrumentation.runtimemetrics.MemoryPools;
import io.opentelemetry.javaagent.extension.AgentListener;
Expand All @@ -28,6 +29,7 @@ public void afterAgent(Config config, AutoConfiguredOpenTelemetrySdk unused) {
.isInstrumentationEnabled(Collections.singleton("runtime-metrics"), DEFAULT_ENABLED)) {

MemoryPools.registerObservers(GlobalOpenTelemetry.get());
Classes.registerObservers(GlobalOpenTelemetry.get());

if (config.getBoolean(
"otel.instrumentation.runtime-metrics.experimental-metrics.enabled", false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class RuntimeMetricsTest extends AgentInstrumentationSpecification {
assert getMetrics().any { it.name == "process.runtime.jvm.memory.usage" }
assert getMetrics().any { it.name == "process.runtime.jvm.memory.committed" }
assert getMetrics().any { it.name == "process.runtime.jvm.memory.max" }
assert getMetrics().any { it.name == "process.runtime.jvm.classes.loaded" }
assert getMetrics().any { it.name == "process.runtime.jvm.classes.unloaded" }
assert getMetrics().any { it.name == "process.runtime.jvm.classes.current_loaded" }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.runtimemetrics;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;

/**
* Registers measurements that generate metrics about JVM classes.
*
* <p>Example usage:
*
* <pre>{@code
* Classes.registerObservers(GlobalOpenTelemetry.get());
* }</pre>
*
* <p>Example metrics being exported:
*
* <pre>
* process.runtime.jvm.classes.loaded 100
* process.runtime.jvm.classes.unloaded 2
* process.runtime.jvm.classes.current_loaded 98
* </pre>
*/
public final class Classes {

// Visible for testing
static final Classes INSTANCE = new Classes();

/** Register observers for java runtime class metrics. */
public static void registerObservers(OpenTelemetry openTelemetry) {
INSTANCE.registerObservers(openTelemetry, ManagementFactory.getClassLoadingMXBean());
}

// Visible for testing
void registerObservers(OpenTelemetry openTelemetry, ClassLoadingMXBean classBean) {
Meter meter = openTelemetry.getMeter("io.opentelemetry.runtime-metrics");

meter
.counterBuilder("process.runtime.jvm.classes.loaded")
.setDescription("Number of classes loaded since JVM start")
.setUnit("1")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getTotalLoadedClassCount()));

meter
.counterBuilder("process.runtime.jvm.classes.unloaded")
.setDescription("Number of classes unloaded since JVM start")
.setUnit("1")
.buildWithCallback(
observableMeasurement ->
observableMeasurement.record(classBean.getUnloadedClassCount()));

meter
.upDownCounterBuilder("process.runtime.jvm.classes.current_loaded")
.setDescription("Number of classes currently loaded")
.setUnit("1")
.buildWithCallback(
observableMeasurement -> observableMeasurement.record(classBean.getLoadedClassCount()));
}

private Classes() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.runtimemetrics;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.Mockito.when;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.lang.management.ClassLoadingMXBean;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class ClassesTest {

@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();

@Mock private ClassLoadingMXBean classBean;

@Test
void registerObservers() {
when(classBean.getTotalLoadedClassCount()).thenReturn(3L);
when(classBean.getUnloadedClassCount()).thenReturn(2L);
when(classBean.getLoadedClassCount()).thenReturn(1);

Classes.INSTANCE.registerObservers(testing.getOpenTelemetry(), classBean);

testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-metrics",
"process.runtime.jvm.classes.loaded",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasDescription("Number of classes loaded since JVM start")
.hasUnit("1")
.hasLongSumSatisfying(
sum ->
sum.isMonotonic()
.hasPointsSatisfying(
point ->
point.hasValue(3).hasAttributes(Attributes.empty())))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-metrics",
"process.runtime.jvm.classes.unloaded",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasDescription("Number of classes unloaded since JVM start")
.hasUnit("1")
.hasLongSumSatisfying(
sum ->
sum.isMonotonic()
.hasPointsSatisfying(
point ->
point.hasValue(2).hasAttributes(Attributes.empty())))));
testing.waitAndAssertMetrics(
"io.opentelemetry.runtime-metrics",
"process.runtime.jvm.classes.current_loaded",
metrics ->
metrics.anySatisfy(
metricData ->
assertThat(metricData)
.hasDescription("Number of classes currently loaded")
.hasUnit("1")
.hasLongSumSatisfying(
sum ->
sum.isNotMonotonic()
.hasPointsSatisfying(
point ->
point.hasValue(1).hasAttributes(Attributes.empty())))));
}
}

0 comments on commit fcf5fb5

Please sign in to comment.