Enable oshi ProcessMetrics in javaagent (and refactor oshi instrumentation) (#5281)

* Enable oshi ProcessMetrics in javaagent (and refactor oshi instrumentation)

* Get rid of double async instrument registration logs

* spotless
This commit is contained in:
Mateusz Rzeszutek 2022-02-01 09:57:48 +01:00 committed by GitHub
parent 8240a5f66f
commit 3bbc41a6ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 259 additions and 149 deletions

View File

@ -17,5 +17,11 @@ dependencies {
library("com.github.oshi:oshi-core:5.3.1")
testImplementation("com.google.guava:guava")
testImplementation(project(":instrumentation:oshi:testing"))
}
tasks {
withType<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.oshi.experimental-metrics.enabled=true")
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.oshi;
import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.instrumentation.oshi.ProcessMetrics;
import io.opentelemetry.instrumentation.oshi.SystemMetrics;
import java.util.concurrent.atomic.AtomicBoolean;
public final class MetricsRegistration {
private static final AtomicBoolean registered = new AtomicBoolean();
public static void register() {
if (registered.compareAndSet(false, true)) {
SystemMetrics.registerObservers();
// ProcessMetrics don't follow the spec
if (Config.get()
.getBoolean("otel.instrumentation.oshi.experimental-metrics.enabled", false)) {
ProcessMetrics.registerObservers();
}
}
}
private MetricsRegistration() {}
}

View File

@ -11,7 +11,6 @@ import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import io.opentelemetry.instrumentation.oshi.SystemMetrics;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
@ -44,7 +43,7 @@ public class SystemInfoInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter() {
SystemMetrics.registerObservers();
MetricsRegistration.register();
}
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.oshi
import com.google.common.base.Stopwatch
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import static java.util.concurrent.TimeUnit.SECONDS
class OshiTest extends AgentInstrumentationSpecification {
def "test system metrics is enabled"() {
expect:
// TODO (trask) is this the instrumentation library name we want?
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.disk.io") != null
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.disk.operations") != null
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.memory.usage") != null
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.memory.utilization") != null
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.network.errors") != null
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.network.io") != null
findMetric("io.opentelemetry.javaagent.shaded.instrumentation.oshi", "system.network.packets") != null
}
def findMetric(instrumentationName, metricName) {
Stopwatch stopwatch = Stopwatch.createStarted()
while (stopwatch.elapsed(SECONDS) < 10) {
for (def metric : metrics) {
if (metric.instrumentationLibraryInfo.name == instrumentationName && metric.name == metricName) {
return metric
}
}
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.oshi;
import io.opentelemetry.instrumentation.oshi.AbstractProcessMetricsTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
class ProcessMetricsTest extends AbstractProcessMetricsTest {
@RegisterExtension
public static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@Override
protected void registerMetrics() {}
@Override
protected InstrumentationExtension testing() {
return testing;
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.oshi;
import io.opentelemetry.instrumentation.oshi.AbstractSystemMetricsTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
class SystemMetricsTest extends AbstractSystemMetricsTest {
@RegisterExtension
public static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@Override
protected void registerMetrics() {}
@Override
protected InstrumentationExtension testing() {
return testing;
}
}

View File

@ -5,7 +5,5 @@ plugins {
dependencies {
library("com.github.oshi:oshi-core:5.3.1")
testImplementation("io.opentelemetry:opentelemetry-sdk-metrics")
testImplementation(project(":testing-common"))
testImplementation("org.assertj:assertj-core")
testImplementation(project(":instrumentation:oshi:testing"))
}

View File

@ -22,7 +22,7 @@ public class ProcessMetrics {
/** Register observers for java runtime metrics. */
public static void registerObservers() {
// TODO(anuraaga): registerObservers should accept an OpenTelemetry instance
Meter meter = GlobalOpenTelemetry.get().getMeterProvider().get(ProcessMetrics.class.getName());
Meter meter = GlobalOpenTelemetry.get().getMeterProvider().get("io.opentelemetry.oshi");
SystemInfo systemInfo = new SystemInfo();
OperatingSystem osInfo = systemInfo.getOperatingSystem();
OSProcess processInfo = osInfo.getProcess(osInfo.getProcessId());
@ -30,7 +30,7 @@ public class ProcessMetrics {
meter
.upDownCounterBuilder("runtime.java.memory")
.setDescription("Runtime Java memory")
.setUnit("bytes")
.setUnit("By")
.buildWithCallback(
r -> {
processInfo.updateAttributes();
@ -41,12 +41,12 @@ public class ProcessMetrics {
meter
.gaugeBuilder("runtime.java.cpu_time")
.setDescription("Runtime Java CPU time")
.setUnit("seconds")
.setUnit("ms")
.buildWithCallback(
r -> {
processInfo.updateAttributes();
r.record(processInfo.getUserTime() * 1000, Attributes.of(TYPE_KEY, "user"));
r.record(processInfo.getKernelTime() * 1000, Attributes.of(TYPE_KEY, "system"));
r.record(processInfo.getUserTime(), Attributes.of(TYPE_KEY, "user"));
r.record(processInfo.getKernelTime(), Attributes.of(TYPE_KEY, "system"));
});
}
}

View File

@ -30,8 +30,7 @@ public class SystemMetrics {
/** Register observers for system metrics. */
public static void registerObservers() {
// TODO(anuraaga): registerObservers should accept an OpenTelemetry instance
Meter meter =
GlobalOpenTelemetry.get().getMeterProvider().get("io.opentelemetry.instrumentation.oshi");
Meter meter = GlobalOpenTelemetry.get().getMeterProvider().get("io.opentelemetry.oshi");
SystemInfo systemInfo = new SystemInfo();
HardwareAbstractionLayer hal = systemInfo.getHardware();

View File

@ -1,55 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.testing.assertj.MetricAssertions;
import io.opentelemetry.sdk.testing.assertj.MetricDataAssert;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import java.util.Collection;
import java.util.function.Consumer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
class AbstractMetricsTest {
static InMemoryMetricReader metricReader;
@BeforeAll
static void initializeOpenTelemetry() {
metricReader = InMemoryMetricReader.create();
OpenTelemetrySdk.builder()
.setMeterProvider(SdkMeterProvider.builder().registerMetricReader(metricReader).build())
.buildAndRegisterGlobal();
}
@AfterAll
static void reset() {
GlobalOpenTelemetry.resetForTest();
}
@SafeVarargs
protected final void waitAndAssertMetrics(Consumer<MetricDataAssert>... assertions) {
await()
.untilAsserted(
() -> {
Collection<MetricData> metrics = metricReader.collectAllMetrics();
assertThat(metrics).isNotEmpty();
for (Consumer<MetricDataAssert> assertion : assertions) {
assertThat(metrics)
.anySatisfy(metric -> assertion.accept(MetricAssertions.assertThat(metric)));
}
});
}
}

View File

@ -5,30 +5,22 @@
package io.opentelemetry.instrumentation.oshi;
import static io.opentelemetry.sdk.testing.assertj.MetricAssertions.assertThat;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.Test;
class ProcessMetricsTest extends AbstractProcessMetricsTest {
public class ProcessMetricsTest extends AbstractMetricsTest {
@RegisterExtension
public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Test
public void test() {
@Override
protected void registerMetrics() {
ProcessMetrics.registerObservers();
}
waitAndAssertMetrics(
metric ->
metric
.hasName("runtime.java.memory")
.hasUnit("bytes")
.hasLongSum()
.points()
.anySatisfy(point -> assertThat(point.getValue()).isPositive()),
metric ->
metric
.hasName("runtime.java.cpu_time")
.hasUnit("seconds")
.hasDoubleGauge()
.points()
.anySatisfy(point -> assertThat(point.getValue()).isPositive()));
@Override
protected InstrumentationExtension testing() {
return testing;
}
}

View File

@ -5,34 +5,22 @@
package io.opentelemetry.instrumentation.oshi;
import static io.opentelemetry.sdk.testing.assertj.MetricAssertions.assertThat;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.Test;
class SystemMetricsTest extends AbstractSystemMetricsTest {
public class SystemMetricsTest extends AbstractMetricsTest {
@RegisterExtension
public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Test
public void test() {
@Override
protected void registerMetrics() {
SystemMetrics.registerObservers();
}
waitAndAssertMetrics(
metric ->
metric
.hasName("system.memory.usage")
.hasUnit("By")
.hasLongSum()
.points()
.anySatisfy(point -> assertThat(point.getValue()).isPositive()),
metric ->
metric
.hasName("system.memory.utilization")
.hasUnit("1")
.hasDoubleGauge()
.points()
.anySatisfy(point -> assertThat(point.getValue()).isPositive()),
metric -> metric.hasName("system.network.io").hasUnit("By").hasLongSum(),
metric -> metric.hasName("system.network.packets").hasUnit("packets").hasLongSum(),
metric -> metric.hasName("system.network.errors").hasUnit("errors").hasLongSum(),
metric -> metric.hasName("system.disk.operations").hasUnit("operations").hasLongSum());
@Override
protected InstrumentationExtension testing() {
return testing;
}
}

View File

@ -0,0 +1,7 @@
plugins {
id("otel.java-conventions")
}
dependencies {
api(project(":testing-common"))
}

View File

@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import static io.opentelemetry.sdk.testing.assertj.MetricAssertions.assertThat;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.junit.jupiter.api.Test;
public abstract class AbstractProcessMetricsTest {
protected abstract void registerMetrics();
protected abstract InstrumentationExtension testing();
@Test
void test() {
// when
registerMetrics();
// then
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"runtime.java.memory",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("By")
.hasLongSum()
.points()
.anySatisfy(point -> assertThat(point.getValue()).isPositive())));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"runtime.java.cpu_time",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("ms")
.hasDoubleGauge()
.points()
.anySatisfy(point -> assertThat(point.getValue()).isPositive())));
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import static io.opentelemetry.sdk.testing.assertj.MetricAssertions.assertThat;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
public abstract class AbstractSystemMetricsTest {
protected abstract void registerMetrics();
protected abstract InstrumentationExtension testing();
@Test
public void test() {
// when
registerMetrics();
// then
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.memory.usage",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("By")
.hasLongSum()
.points()
.anySatisfy(
point -> Assertions.assertThat(point.getValue()).isPositive())));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.memory.utilization",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("1")
.hasDoubleGauge()
.points()
.anySatisfy(
point -> Assertions.assertThat(point.getValue()).isPositive())));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.network.io",
metrics -> metrics.anySatisfy(metric -> assertThat(metric).hasUnit("By").hasLongSum()));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.network.packets",
metrics ->
metrics.anySatisfy(metric -> assertThat(metric).hasUnit("packets").hasLongSum()));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.network.errors",
metrics ->
metrics.anySatisfy(metric -> assertThat(metric).hasUnit("errors").hasLongSum()));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.disk.io",
metrics -> metrics.anySatisfy(metric -> assertThat(metric).hasUnit("By").hasLongSum()));
testing()
.waitAndAssertMetrics(
"io.opentelemetry.oshi",
"system.disk.operations",
metrics ->
metrics.anySatisfy(
metric -> assertThat(metric).hasUnit("operations").hasLongSum()));
}
}

View File

@ -310,6 +310,7 @@ include(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent")
include(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent")
include(":instrumentation:oshi:javaagent")
include(":instrumentation:oshi:library")
include(":instrumentation:oshi:testing")
include(":instrumentation:play:play-2.4:javaagent")
include(":instrumentation:play:play-2.6:javaagent")
include(":instrumentation:play-ws:play-ws-1.0:javaagent")