Skip to content

Commit 5f6232f

Browse files
authored
feat(oshi): oshi metrics observables (#10364)
1 parent 980d8ea commit 5f6232f

File tree

5 files changed

+213
-127
lines changed

5 files changed

+213
-127
lines changed

instrumentation/oshi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oshi/MetricsRegistration.java

+19-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.opentelemetry.instrumentation.oshi.ProcessMetrics;
1010
import io.opentelemetry.instrumentation.oshi.SystemMetrics;
1111
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
12+
import java.util.ArrayList;
13+
import java.util.List;
1214
import java.util.concurrent.atomic.AtomicBoolean;
1315

1416
public final class MetricsRegistration {
@@ -17,13 +19,28 @@ public final class MetricsRegistration {
1719

1820
public static void register() {
1921
if (registered.compareAndSet(false, true)) {
20-
SystemMetrics.registerObservers(GlobalOpenTelemetry.get());
22+
List<AutoCloseable> observables = new ArrayList<>();
23+
observables.addAll(SystemMetrics.registerObservers(GlobalOpenTelemetry.get()));
2124

2225
// ProcessMetrics don't follow the spec
2326
if (InstrumentationConfig.get()
2427
.getBoolean("otel.instrumentation.oshi.experimental-metrics.enabled", false)) {
25-
ProcessMetrics.registerObservers(GlobalOpenTelemetry.get());
28+
observables.addAll(ProcessMetrics.registerObservers(GlobalOpenTelemetry.get()));
2629
}
30+
Thread cleanupTelemetry = new Thread(() -> MetricsRegistration.closeObservables(observables));
31+
Runtime.getRuntime().addShutdownHook(cleanupTelemetry);
32+
}
33+
}
34+
35+
private static void closeObservables(List<AutoCloseable> observables) {
36+
observables.forEach(MetricsRegistration::closeObservable);
37+
}
38+
39+
private static void closeObservable(AutoCloseable observable) {
40+
try {
41+
observable.close();
42+
} catch (Exception e) {
43+
throw new IllegalStateException("Error occurred closing observable", e);
2744
}
2845
}
2946

instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/ProcessMetrics.java

+28-23
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.opentelemetry.api.common.AttributeKey;
1010
import io.opentelemetry.api.common.Attributes;
1111
import io.opentelemetry.api.metrics.Meter;
12+
import java.util.ArrayList;
13+
import java.util.List;
1214
import oshi.SystemInfo;
1315
import oshi.software.os.OSProcess;
1416
import oshi.software.os.OperatingSystem;
@@ -20,33 +22,36 @@ public class ProcessMetrics {
2022
private ProcessMetrics() {}
2123

2224
/** Register observers for java runtime metrics. */
23-
public static void registerObservers(OpenTelemetry openTelemetry) {
25+
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
2426
Meter meter = openTelemetry.getMeterProvider().get("io.opentelemetry.oshi");
2527
SystemInfo systemInfo = new SystemInfo();
2628
OperatingSystem osInfo = systemInfo.getOperatingSystem();
2729
OSProcess processInfo = osInfo.getProcess(osInfo.getProcessId());
30+
List<AutoCloseable> observables = new ArrayList<>();
31+
observables.add(
32+
meter
33+
.upDownCounterBuilder("runtime.java.memory")
34+
.setDescription("Runtime Java memory")
35+
.setUnit("By")
36+
.buildWithCallback(
37+
r -> {
38+
processInfo.updateAttributes();
39+
r.record(processInfo.getResidentSetSize(), Attributes.of(TYPE_KEY, "rss"));
40+
r.record(processInfo.getVirtualSize(), Attributes.of(TYPE_KEY, "vms"));
41+
}));
2842

29-
meter
30-
.upDownCounterBuilder("runtime.java.memory")
31-
.setDescription("Runtime Java memory")
32-
.setUnit("By")
33-
.buildWithCallback(
34-
r -> {
35-
processInfo.updateAttributes();
36-
r.record(processInfo.getResidentSetSize(), Attributes.of(TYPE_KEY, "rss"));
37-
r.record(processInfo.getVirtualSize(), Attributes.of(TYPE_KEY, "vms"));
38-
});
39-
40-
meter
41-
.gaugeBuilder("runtime.java.cpu_time")
42-
.setDescription("Runtime Java CPU time")
43-
.setUnit("ms")
44-
.ofLongs()
45-
.buildWithCallback(
46-
r -> {
47-
processInfo.updateAttributes();
48-
r.record(processInfo.getUserTime(), Attributes.of(TYPE_KEY, "user"));
49-
r.record(processInfo.getKernelTime(), Attributes.of(TYPE_KEY, "system"));
50-
});
43+
observables.add(
44+
meter
45+
.gaugeBuilder("runtime.java.cpu_time")
46+
.setDescription("Runtime Java CPU time")
47+
.setUnit("ms")
48+
.ofLongs()
49+
.buildWithCallback(
50+
r -> {
51+
processInfo.updateAttributes();
52+
r.record(processInfo.getUserTime(), Attributes.of(TYPE_KEY, "user"));
53+
r.record(processInfo.getKernelTime(), Attributes.of(TYPE_KEY, "system"));
54+
}));
55+
return observables;
5156
}
5257
}

instrumentation/oshi/library/src/main/java/io/opentelemetry/instrumentation/oshi/SystemMetrics.java

+108-96
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.opentelemetry.api.common.AttributeKey;
1010
import io.opentelemetry.api.common.Attributes;
1111
import io.opentelemetry.api.metrics.Meter;
12+
import java.util.ArrayList;
13+
import java.util.List;
1214
import oshi.SystemInfo;
1315
import oshi.hardware.GlobalMemory;
1416
import oshi.hardware.HWDiskStore;
@@ -28,111 +30,121 @@ public class SystemMetrics {
2830
private SystemMetrics() {}
2931

3032
/** Register observers for system metrics. */
31-
public static void registerObservers(OpenTelemetry openTelemetry) {
33+
public static List<AutoCloseable> registerObservers(OpenTelemetry openTelemetry) {
3234
Meter meter = openTelemetry.getMeterProvider().get("io.opentelemetry.oshi");
3335
SystemInfo systemInfo = new SystemInfo();
3436
HardwareAbstractionLayer hal = systemInfo.getHardware();
37+
List<AutoCloseable> observables = new ArrayList<>();
3538

36-
meter
37-
.upDownCounterBuilder("system.memory.usage")
38-
.setDescription("System memory usage")
39-
.setUnit("By")
40-
.buildWithCallback(
41-
r -> {
42-
GlobalMemory mem = hal.getMemory();
43-
r.record(mem.getTotal() - mem.getAvailable(), ATTRIBUTES_USED);
44-
r.record(mem.getAvailable(), ATTRIBUTES_FREE);
45-
});
39+
observables.add(
40+
meter
41+
.upDownCounterBuilder("system.memory.usage")
42+
.setDescription("System memory usage")
43+
.setUnit("By")
44+
.buildWithCallback(
45+
r -> {
46+
GlobalMemory mem = hal.getMemory();
47+
r.record(mem.getTotal() - mem.getAvailable(), ATTRIBUTES_USED);
48+
r.record(mem.getAvailable(), ATTRIBUTES_FREE);
49+
}));
4650

47-
meter
48-
.gaugeBuilder("system.memory.utilization")
49-
.setDescription("System memory utilization")
50-
.setUnit("1")
51-
.buildWithCallback(
52-
r -> {
53-
GlobalMemory mem = hal.getMemory();
54-
r.record(
55-
((double) (mem.getTotal() - mem.getAvailable())) / mem.getTotal(),
56-
ATTRIBUTES_USED);
57-
r.record(((double) mem.getAvailable()) / mem.getTotal(), ATTRIBUTES_FREE);
58-
});
51+
observables.add(
52+
meter
53+
.gaugeBuilder("system.memory.utilization")
54+
.setDescription("System memory utilization")
55+
.setUnit("1")
56+
.buildWithCallback(
57+
r -> {
58+
GlobalMemory mem = hal.getMemory();
59+
r.record(
60+
((double) (mem.getTotal() - mem.getAvailable())) / mem.getTotal(),
61+
ATTRIBUTES_USED);
62+
r.record(((double) mem.getAvailable()) / mem.getTotal(), ATTRIBUTES_FREE);
63+
}));
5964

60-
meter
61-
.counterBuilder("system.network.io")
62-
.setDescription("System network IO")
63-
.setUnit("By")
64-
.buildWithCallback(
65-
r -> {
66-
for (NetworkIF networkIf : hal.getNetworkIFs()) {
67-
networkIf.updateAttributes();
68-
long recv = networkIf.getBytesRecv();
69-
long sent = networkIf.getBytesSent();
70-
String device = networkIf.getName();
71-
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
72-
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
73-
}
74-
});
65+
observables.add(
66+
meter
67+
.counterBuilder("system.network.io")
68+
.setDescription("System network IO")
69+
.setUnit("By")
70+
.buildWithCallback(
71+
r -> {
72+
for (NetworkIF networkIf : hal.getNetworkIFs()) {
73+
networkIf.updateAttributes();
74+
long recv = networkIf.getBytesRecv();
75+
long sent = networkIf.getBytesSent();
76+
String device = networkIf.getName();
77+
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
78+
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
79+
}
80+
}));
7581

76-
meter
77-
.counterBuilder("system.network.packets")
78-
.setDescription("System network packets")
79-
.setUnit("{packets}")
80-
.buildWithCallback(
81-
r -> {
82-
for (NetworkIF networkIf : hal.getNetworkIFs()) {
83-
networkIf.updateAttributes();
84-
long recv = networkIf.getPacketsRecv();
85-
long sent = networkIf.getPacketsSent();
86-
String device = networkIf.getName();
87-
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
88-
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
89-
}
90-
});
82+
observables.add(
83+
meter
84+
.counterBuilder("system.network.packets")
85+
.setDescription("System network packets")
86+
.setUnit("{packets}")
87+
.buildWithCallback(
88+
r -> {
89+
for (NetworkIF networkIf : hal.getNetworkIFs()) {
90+
networkIf.updateAttributes();
91+
long recv = networkIf.getPacketsRecv();
92+
long sent = networkIf.getPacketsSent();
93+
String device = networkIf.getName();
94+
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
95+
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
96+
}
97+
}));
9198

92-
meter
93-
.counterBuilder("system.network.errors")
94-
.setDescription("System network errors")
95-
.setUnit("{errors}")
96-
.buildWithCallback(
97-
r -> {
98-
for (NetworkIF networkIf : hal.getNetworkIFs()) {
99-
networkIf.updateAttributes();
100-
long recv = networkIf.getInErrors();
101-
long sent = networkIf.getOutErrors();
102-
String device = networkIf.getName();
103-
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
104-
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
105-
}
106-
});
99+
observables.add(
100+
meter
101+
.counterBuilder("system.network.errors")
102+
.setDescription("System network errors")
103+
.setUnit("{errors}")
104+
.buildWithCallback(
105+
r -> {
106+
for (NetworkIF networkIf : hal.getNetworkIFs()) {
107+
networkIf.updateAttributes();
108+
long recv = networkIf.getInErrors();
109+
long sent = networkIf.getOutErrors();
110+
String device = networkIf.getName();
111+
r.record(recv, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "receive"));
112+
r.record(sent, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "transmit"));
113+
}
114+
}));
107115

108-
meter
109-
.counterBuilder("system.disk.io")
110-
.setDescription("System disk IO")
111-
.setUnit("By")
112-
.buildWithCallback(
113-
r -> {
114-
for (HWDiskStore diskStore : hal.getDiskStores()) {
115-
long read = diskStore.getReadBytes();
116-
long write = diskStore.getWriteBytes();
117-
String device = diskStore.getName();
118-
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
119-
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
120-
}
121-
});
116+
observables.add(
117+
meter
118+
.counterBuilder("system.disk.io")
119+
.setDescription("System disk IO")
120+
.setUnit("By")
121+
.buildWithCallback(
122+
r -> {
123+
for (HWDiskStore diskStore : hal.getDiskStores()) {
124+
long read = diskStore.getReadBytes();
125+
long write = diskStore.getWriteBytes();
126+
String device = diskStore.getName();
127+
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
128+
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
129+
}
130+
}));
122131

123-
meter
124-
.counterBuilder("system.disk.operations")
125-
.setDescription("System disk operations")
126-
.setUnit("{operations}")
127-
.buildWithCallback(
128-
r -> {
129-
for (HWDiskStore diskStore : hal.getDiskStores()) {
130-
long read = diskStore.getReads();
131-
long write = diskStore.getWrites();
132-
String device = diskStore.getName();
133-
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
134-
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
135-
}
136-
});
132+
observables.add(
133+
meter
134+
.counterBuilder("system.disk.operations")
135+
.setDescription("System disk operations")
136+
.setUnit("{operations}")
137+
.buildWithCallback(
138+
r -> {
139+
for (HWDiskStore diskStore : hal.getDiskStores()) {
140+
long read = diskStore.getReads();
141+
long write = diskStore.getWrites();
142+
String device = diskStore.getName();
143+
r.record(read, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "read"));
144+
r.record(write, Attributes.of(DEVICE_KEY, device, DIRECTION_KEY, "write"));
145+
}
146+
}));
147+
148+
return observables;
137149
}
138150
}

instrumentation/oshi/library/src/test/java/io/opentelemetry/instrumentation/oshi/ProcessMetricsTest.java

+29-3
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,46 @@
88
import io.opentelemetry.api.GlobalOpenTelemetry;
99
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
1010
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
11+
import java.util.List;
12+
import org.assertj.core.api.Assertions;
13+
import org.junit.jupiter.api.AfterAll;
14+
import org.junit.jupiter.api.BeforeAll;
15+
import org.junit.jupiter.api.Test;
1116
import org.junit.jupiter.api.extension.RegisterExtension;
1217

1318
class ProcessMetricsTest extends AbstractProcessMetricsTest {
1419

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

18-
@Override
19-
protected void registerMetrics() {
20-
ProcessMetrics.registerObservers(GlobalOpenTelemetry.get());
23+
private static List<AutoCloseable> observables;
24+
25+
@BeforeAll
26+
static void setUp() {
27+
observables = ProcessMetrics.registerObservers(GlobalOpenTelemetry.get());
28+
}
29+
30+
@AfterAll
31+
static void tearDown() {
32+
for (AutoCloseable observable : observables) {
33+
try {
34+
observable.close();
35+
} catch (Exception e) {
36+
// ignore
37+
}
38+
}
2139
}
2240

41+
@Override
42+
protected void registerMetrics() {}
43+
2344
@Override
2445
protected InstrumentationExtension testing() {
2546
return testing;
2647
}
48+
49+
@Test
50+
void verifyObservablesAreNotEmpty() {
51+
Assertions.assertThat(observables).as("List of observables").isNotEmpty();
52+
}
2753
}

0 commit comments

Comments
 (0)