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:
parent
8240a5f66f
commit
3bbc41a6ce
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
id("otel.java-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":testing-common"))
|
||||
}
|
|
@ -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())));
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue