Skip to content

feat(oshi): oshi metrics observables #10364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io.opentelemetry.instrumentation.oshi.ProcessMetrics;
import io.opentelemetry.instrumentation.oshi.SystemMetrics;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public final class MetricsRegistration {
Expand All @@ -17,13 +19,28 @@ public final class MetricsRegistration {

public static void register() {
if (registered.compareAndSet(false, true)) {
SystemMetrics.registerObservers(GlobalOpenTelemetry.get());
List<AutoCloseable> observables = new ArrayList<>();
observables.addAll(SystemMetrics.registerObservers(GlobalOpenTelemetry.get()));

// ProcessMetrics don't follow the spec
if (InstrumentationConfig.get()
.getBoolean("otel.instrumentation.oshi.experimental-metrics.enabled", false)) {
ProcessMetrics.registerObservers(GlobalOpenTelemetry.get());
observables.addAll(ProcessMetrics.registerObservers(GlobalOpenTelemetry.get()));
}
Thread cleanupTelemetry = new Thread(() -> MetricsRegistration.closeObservables(observables));
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
}
}

private static void closeObservables(List<AutoCloseable> observables) {
observables.forEach(MetricsRegistration::closeObservable);
}

private static void closeObservable(AutoCloseable observable) {
try {
observable.close();
} catch (Exception e) {
throw new IllegalStateException("Error occurred closing observable", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import java.util.ArrayList;
import java.util.List;
import oshi.SystemInfo;
import oshi.software.os.OSProcess;
import oshi.software.os.OperatingSystem;
Expand All @@ -20,33 +22,36 @@ public class ProcessMetrics {
private ProcessMetrics() {}

/** Register observers for java runtime metrics. */
public static void registerObservers(OpenTelemetry openTelemetry) {
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
Meter meter = openTelemetry.getMeterProvider().get("io.opentelemetry.oshi");
SystemInfo systemInfo = new SystemInfo();
OperatingSystem osInfo = systemInfo.getOperatingSystem();
OSProcess processInfo = osInfo.getProcess(osInfo.getProcessId());
List<AutoCloseable> observables = new ArrayList<>();
observables.add(
meter
.upDownCounterBuilder("runtime.java.memory")
.setDescription("Runtime Java memory")
.setUnit("By")
.buildWithCallback(
r -> {
processInfo.updateAttributes();
r.record(processInfo.getResidentSetSize(), Attributes.of(TYPE_KEY, "rss"));
r.record(processInfo.getVirtualSize(), Attributes.of(TYPE_KEY, "vms"));
}));

meter
.upDownCounterBuilder("runtime.java.memory")
.setDescription("Runtime Java memory")
.setUnit("By")
.buildWithCallback(
r -> {
processInfo.updateAttributes();
r.record(processInfo.getResidentSetSize(), Attributes.of(TYPE_KEY, "rss"));
r.record(processInfo.getVirtualSize(), Attributes.of(TYPE_KEY, "vms"));
});

meter
.gaugeBuilder("runtime.java.cpu_time")
.setDescription("Runtime Java CPU time")
.setUnit("ms")
.ofLongs()
.buildWithCallback(
r -> {
processInfo.updateAttributes();
r.record(processInfo.getUserTime(), Attributes.of(TYPE_KEY, "user"));
r.record(processInfo.getKernelTime(), Attributes.of(TYPE_KEY, "system"));
});
observables.add(
meter
.gaugeBuilder("runtime.java.cpu_time")
.setDescription("Runtime Java CPU time")
.setUnit("ms")
.ofLongs()
.buildWithCallback(
r -> {
processInfo.updateAttributes();
r.record(processInfo.getUserTime(), Attributes.of(TYPE_KEY, "user"));
r.record(processInfo.getKernelTime(), Attributes.of(TYPE_KEY, "system"));
}));
return observables;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import java.util.ArrayList;
import java.util.List;
import oshi.SystemInfo;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HWDiskStore;
Expand All @@ -28,111 +30,121 @@ public class SystemMetrics {
private SystemMetrics() {}

/** Register observers for system metrics. */
public static void registerObservers(OpenTelemetry openTelemetry) {
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
Meter meter = openTelemetry.getMeterProvider().get("io.opentelemetry.oshi");
SystemInfo systemInfo = new SystemInfo();
HardwareAbstractionLayer hal = systemInfo.getHardware();
List<AutoCloseable> observables = new ArrayList<>();

meter
.upDownCounterBuilder("system.memory.usage")
.setDescription("System memory usage")
.setUnit("By")
.buildWithCallback(
r -> {
GlobalMemory mem = hal.getMemory();
r.record(mem.getTotal() - mem.getAvailable(), ATTRIBUTES_USED);
r.record(mem.getAvailable(), ATTRIBUTES_FREE);
});
observables.add(
meter
.upDownCounterBuilder("system.memory.usage")
.setDescription("System memory usage")
.setUnit("By")
.buildWithCallback(
r -> {
GlobalMemory mem = hal.getMemory();
r.record(mem.getTotal() - mem.getAvailable(), ATTRIBUTES_USED);
r.record(mem.getAvailable(), ATTRIBUTES_FREE);
}));

meter
.gaugeBuilder("system.memory.utilization")
.setDescription("System memory utilization")
.setUnit("1")
.buildWithCallback(
r -> {
GlobalMemory mem = hal.getMemory();
r.record(
((double) (mem.getTotal() - mem.getAvailable())) / mem.getTotal(),
ATTRIBUTES_USED);
r.record(((double) mem.getAvailable()) / mem.getTotal(), ATTRIBUTES_FREE);
});
observables.add(
meter
.gaugeBuilder("system.memory.utilization")
.setDescription("System memory utilization")
.setUnit("1")
.buildWithCallback(
r -> {
GlobalMemory mem = hal.getMemory();
r.record(
((double) (mem.getTotal() - mem.getAvailable())) / mem.getTotal(),
ATTRIBUTES_USED);
r.record(((double) mem.getAvailable()) / mem.getTotal(), ATTRIBUTES_FREE);
}));

meter
.counterBuilder("system.network.io")
.setDescription("System network IO")
.setUnit("By")
.buildWithCallback(
r -> {
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
long recv = networkIf.getBytesRecv();
long sent = networkIf.getBytesSent();
String device = networkIf.getName();
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
}
});
observables.add(
meter
.counterBuilder("system.network.io")
.setDescription("System network IO")
.setUnit("By")
.buildWithCallback(
r -> {
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
long recv = networkIf.getBytesRecv();
long sent = networkIf.getBytesSent();
String device = networkIf.getName();
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
}
}));

meter
.counterBuilder("system.network.packets")
.setDescription("System network packets")
.setUnit("{packets}")
.buildWithCallback(
r -> {
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
long recv = networkIf.getPacketsRecv();
long sent = networkIf.getPacketsSent();
String device = networkIf.getName();
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
}
});
observables.add(
meter
.counterBuilder("system.network.packets")
.setDescription("System network packets")
.setUnit("{packets}")
.buildWithCallback(
r -> {
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
long recv = networkIf.getPacketsRecv();
long sent = networkIf.getPacketsSent();
String device = networkIf.getName();
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
}
}));

meter
.counterBuilder("system.network.errors")
.setDescription("System network errors")
.setUnit("{errors}")
.buildWithCallback(
r -> {
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
long recv = networkIf.getInErrors();
long sent = networkIf.getOutErrors();
String device = networkIf.getName();
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
}
});
observables.add(
meter
.counterBuilder("system.network.errors")
.setDescription("System network errors")
.setUnit("{errors}")
.buildWithCallback(
r -> {
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
long recv = networkIf.getInErrors();
long sent = networkIf.getOutErrors();
String device = networkIf.getName();
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
}
}));

meter
.counterBuilder("system.disk.io")
.setDescription("System disk IO")
.setUnit("By")
.buildWithCallback(
r -> {
for (HWDiskStore diskStore : hal.getDiskStores()) {
long read = diskStore.getReadBytes();
long write = diskStore.getWriteBytes();
String device = diskStore.getName();
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
}
});
observables.add(
meter
.counterBuilder("system.disk.io")
.setDescription("System disk IO")
.setUnit("By")
.buildWithCallback(
r -> {
for (HWDiskStore diskStore : hal.getDiskStores()) {
long read = diskStore.getReadBytes();
long write = diskStore.getWriteBytes();
String device = diskStore.getName();
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
}
}));

meter
.counterBuilder("system.disk.operations")
.setDescription("System disk operations")
.setUnit("{operations}")
.buildWithCallback(
r -> {
for (HWDiskStore diskStore : hal.getDiskStores()) {
long read = diskStore.getReads();
long write = diskStore.getWrites();
String device = diskStore.getName();
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
}
});
observables.add(
meter
.counterBuilder("system.disk.operations")
.setDescription("System disk operations")
.setUnit("{operations}")
.buildWithCallback(
r -> {
for (HWDiskStore diskStore : hal.getDiskStores()) {
long read = diskStore.getReads();
long write = diskStore.getWrites();
String device = diskStore.getName();
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
}
}));

return observables;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,46 @@
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

class ProcessMetricsTest extends AbstractProcessMetricsTest {

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

@Override
protected void registerMetrics() {
ProcessMetrics.registerObservers(GlobalOpenTelemetry.get());
private static List<AutoCloseable> observables;

@BeforeAll
static void setUp() {
observables = ProcessMetrics.registerObservers(GlobalOpenTelemetry.get());
}

@AfterAll
static void tearDown() {
for (AutoCloseable observable : observables) {
try {
observable.close();
} catch (Exception e) {
// ignore
}
}
}

@Override
protected void registerMetrics() {}

@Override
protected InstrumentationExtension testing() {
return testing;
}

@Test
void verifyObservablesAreNotEmpty() {
Assertions.assertThat(observables).as("List of observables").isNotEmpty();
}
}
Loading