Skip to content

Commit

Permalink
feat(oshi): oshi metrics observables (#10364)
Browse files Browse the repository at this point in the history
  • Loading branch information
manikmagar committed Feb 2, 2024
1 parent 980d8ea commit 5f6232f
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 127 deletions.
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

0 comments on commit 5f6232f

Please sign in to comment.