Add system metrics (#1309)

Signed-off-by: Sergei Malafeev <sergei@malafeev.org>
This commit is contained in:
Sergei Malafeev 2020-10-27 23:48:36 +08:00 committed by GitHub
parent 54a85f7501
commit d2813c838d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 411 additions and 0 deletions

View File

@ -0,0 +1,9 @@
apply from: "$rootDir/gradle/instrumentation-library.gradle"
dependencies {
library group: 'com.github.oshi', name: 'oshi-core', version: '5.3.1'
testImplementation project(':testing-common')
testImplementation group: 'org.assertj', name: 'assertj-core', version: '1.7.1'
}

View File

@ -0,0 +1,61 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.common.Labels;
import io.opentelemetry.metrics.AsynchronousInstrument.Callback;
import io.opentelemetry.metrics.AsynchronousInstrument.DoubleResult;
import io.opentelemetry.metrics.AsynchronousInstrument.LongResult;
import io.opentelemetry.metrics.Meter;
import oshi.SystemInfo;
import oshi.software.os.OSProcess;
import oshi.software.os.OperatingSystem;
/** Java Runtime Metrics Utility */
public class ProcessMetrics {
private static final String TYPE_LABEL_KEY = "type";
private ProcessMetrics() {}
/** Register observers for java runtime metrics */
public static void registerObservers() {
Meter meter = OpenTelemetry.getGlobalMeterProvider().get(ProcessMetrics.class.getName());
SystemInfo systemInfo = new SystemInfo();
OperatingSystem osInfo = systemInfo.getOperatingSystem();
OSProcess processInfo = osInfo.getProcess(osInfo.getProcessId());
meter
.longUpDownSumObserverBuilder("runtime.java.memory")
.setDescription("Runtime Java memory")
.setUnit("bytes")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
processInfo.updateAttributes();
r.observe(processInfo.getResidentSetSize(), Labels.of(TYPE_LABEL_KEY, "rss"));
r.observe(processInfo.getVirtualSize(), Labels.of(TYPE_LABEL_KEY, "vms"));
}
});
meter
.doubleValueObserverBuilder("runtime.java.cpu_time")
.setDescription("Runtime Java CPU time")
.setUnit("seconds")
.build()
.setCallback(
new Callback<DoubleResult>() {
@Override
public void update(DoubleResult r) {
processInfo.updateAttributes();
r.observe(processInfo.getUserTime() * 1000, Labels.of(TYPE_LABEL_KEY, "user"));
r.observe(processInfo.getKernelTime() * 1000, Labels.of(TYPE_LABEL_KEY, "system"));
}
});
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.common.Labels;
import io.opentelemetry.metrics.AsynchronousInstrument.Callback;
import io.opentelemetry.metrics.AsynchronousInstrument.DoubleResult;
import io.opentelemetry.metrics.AsynchronousInstrument.LongResult;
import io.opentelemetry.metrics.Meter;
import oshi.SystemInfo;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HWDiskStore;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
/** System Metrics Utility */
public class SystemMetrics {
private static final String TYPE_LABEL_KEY = "type";
private SystemMetrics() {}
/** Register observers for system metrics */
public static void registerObservers() {
Meter meter = OpenTelemetry.getGlobalMeterProvider().get(SystemMetrics.class.getName());
SystemInfo systemInfo = new SystemInfo();
HardwareAbstractionLayer hal = systemInfo.getHardware();
meter
.longUpDownSumObserverBuilder("system.memory.usage")
.setDescription("System memory usage")
.setUnit("bytes")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
GlobalMemory mem = hal.getMemory();
r.observe(mem.getTotal() - mem.getAvailable(), Labels.of(TYPE_LABEL_KEY, "used"));
r.observe(mem.getAvailable(), Labels.of(TYPE_LABEL_KEY, "free"));
}
});
meter
.doubleValueObserverBuilder("system.memory.utilization")
.setDescription("System memory utilization")
.setUnit("1")
.build()
.setCallback(
new Callback<DoubleResult>() {
@Override
public void update(DoubleResult r) {
GlobalMemory mem = hal.getMemory();
r.observe(
((double) (mem.getTotal() - mem.getAvailable())) / mem.getTotal(),
Labels.of(TYPE_LABEL_KEY, "used"));
r.observe(
((double) mem.getAvailable()) / mem.getTotal(),
Labels.of(TYPE_LABEL_KEY, "free"));
}
});
meter
.longSumObserverBuilder("system.network.io")
.setDescription("System network IO")
.setUnit("bytes")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
long recv = 0;
long sent = 0;
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
recv += networkIf.getBytesRecv();
sent += networkIf.getBytesSent();
}
r.observe(recv, Labels.of(TYPE_LABEL_KEY, "receive"));
r.observe(sent, Labels.of(TYPE_LABEL_KEY, "transmit"));
}
});
meter
.longSumObserverBuilder("system.network.packets")
.setDescription("System network packets")
.setUnit("packets")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
long recv = 0;
long sent = 0;
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
recv += networkIf.getPacketsRecv();
sent += networkIf.getPacketsSent();
}
r.observe(recv, Labels.of(TYPE_LABEL_KEY, "receive"));
r.observe(sent, Labels.of(TYPE_LABEL_KEY, "transmit"));
}
});
meter
.longSumObserverBuilder("system.network.errors")
.setDescription("System network errors")
.setUnit("errors")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
long recv = 0;
long sent = 0;
for (NetworkIF networkIf : hal.getNetworkIFs()) {
networkIf.updateAttributes();
recv += networkIf.getInErrors();
sent += networkIf.getOutErrors();
}
r.observe(recv, Labels.of(TYPE_LABEL_KEY, "receive"));
r.observe(sent, Labels.of(TYPE_LABEL_KEY, "transmit"));
}
});
meter
.longSumObserverBuilder("system.disk.io")
.setDescription("System disk IO")
.setUnit("bytes")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
long read = 0;
long write = 0;
for (HWDiskStore diskStore : hal.getDiskStores()) {
read += diskStore.getReadBytes();
write += diskStore.getWriteBytes();
}
r.observe(read, Labels.of(TYPE_LABEL_KEY, "read"));
r.observe(write, Labels.of(TYPE_LABEL_KEY, "write"));
}
});
meter
.longSumObserverBuilder("system.disk.operations")
.setDescription("System disk operations")
.setUnit("operations")
.build()
.setCallback(
new Callback<LongResult>() {
@Override
public void update(LongResult r) {
long read = 0;
long write = 0;
for (HWDiskStore diskStore : hal.getDiskStores()) {
read += diskStore.getReads();
write += diskStore.getWrites();
}
r.observe(read, Labels.of(TYPE_LABEL_KEY, "read"));
r.observe(write, Labels.of(TYPE_LABEL_KEY, "write"));
}
});
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.data.MetricData.DoublePoint;
import io.opentelemetry.sdk.metrics.data.MetricData.LongPoint;
import io.opentelemetry.sdk.metrics.data.MetricData.Point;
import io.opentelemetry.sdk.metrics.data.MetricData.SummaryPoint;
import io.opentelemetry.sdk.metrics.data.MetricData.Type;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
public class AbstractMetricsTest {
TestMetricExporter testMetricExporter;
@BeforeEach
public void beforeEach() {
testMetricExporter = new TestMetricExporter();
}
IntervalMetricReader createIntervalMetricReader() {
return IntervalMetricReader.builder()
.setExportIntervalMillis(100)
.setMetricExporter(testMetricExporter)
.setMetricProducers(
Collections.singletonList(
OpenTelemetrySdk.getGlobalMeterProvider().getMetricProducer()))
.build();
}
public void verify(
String metricName, String unit, MetricData.Type type, boolean checkNonZeroValue) {
List<MetricData> metricDataList = testMetricExporter.metricDataList;
for (MetricData metricData : metricDataList) {
if (metricData.getName().equals(metricName)) {
assertThat(metricData.getDescription()).isNotEmpty();
assertThat(metricData.getUnit()).isEqualTo(unit);
assertThat(metricData.getPoints()).isNotEmpty();
assertThat(metricData.getType()).isEqualTo(type);
if (checkNonZeroValue) {
for (Point point : metricData.getPoints()) {
if (metricData.getType() == Type.NON_MONOTONIC_LONG) {
LongPoint longPoint = (LongPoint) point;
assertThat(longPoint.getValue()).isGreaterThan(0);
} else if (metricData.getType() == Type.NON_MONOTONIC_DOUBLE) {
DoublePoint doublePoint = (DoublePoint) point;
assertThat(doublePoint.getValue()).isGreaterThan(0.0);
} else if (metricData.getType() == Type.SUMMARY) {
SummaryPoint summaryPoint = (SummaryPoint) point;
assertThat(summaryPoint.getSum()).isGreaterThan(0.0);
} else if (metricData.getType() == Type.GAUGE_DOUBLE) {
DoublePoint doublePoint = (DoublePoint) point;
assertThat(doublePoint.getValue()).isGreaterThan(0.0);
} else {
Assertions.fail("unexpected type " + metricData.getType());
}
}
}
return;
}
}
Assertions.fail("No metric for " + metricName);
}
static class TestMetricExporter implements MetricExporter {
private final List<MetricData> metricDataList = new CopyOnWriteArrayList<>();
private final CountDownLatch latch = new CountDownLatch(1);
@Override
public CompletableResultCode export(Collection<MetricData> collection) {
metricDataList.addAll(collection);
latch.countDown();
return CompletableResultCode.ofSuccess();
}
@Override
public CompletableResultCode flush() {
return CompletableResultCode.ofSuccess();
}
@Override
public void shutdown() {}
public void waitForData() throws InterruptedException {
latch.await(1, TimeUnit.SECONDS);
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import io.opentelemetry.sdk.metrics.data.MetricData.Type;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
import org.junit.jupiter.api.Test;
public class ProcessMetricsTest extends AbstractMetricsTest {
@Test
public void test() throws Exception {
ProcessMetrics.registerObservers();
IntervalMetricReader intervalMetricReader = createIntervalMetricReader();
testMetricExporter.waitForData();
intervalMetricReader.shutdown();
verify("runtime.java.memory", "bytes", Type.NON_MONOTONIC_LONG, true);
verify("runtime.java.cpu_time", "seconds", Type.GAUGE_DOUBLE, true);
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.oshi;
import io.opentelemetry.sdk.metrics.data.MetricData.Type;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
import org.junit.jupiter.api.Test;
public class SystemMetricsTest extends AbstractMetricsTest {
@Test
public void test() throws Exception {
SystemMetrics.registerObservers();
IntervalMetricReader intervalMetricReader = createIntervalMetricReader();
testMetricExporter.waitForData();
intervalMetricReader.shutdown();
verify("system.memory.usage", "bytes", Type.NON_MONOTONIC_LONG, true);
verify("system.memory.utilization", "1", Type.GAUGE_DOUBLE, true);
verify("system.network.io", "bytes", Type.MONOTONIC_LONG, false);
verify("system.network.packets", "packets", Type.MONOTONIC_LONG, false);
verify("system.network.errors", "errors", Type.MONOTONIC_LONG, false);
verify("system.disk.io", "bytes", Type.MONOTONIC_LONG, false);
verify("system.disk.operations", "operations", Type.MONOTONIC_LONG, false);
}
}

View File

@ -148,6 +148,7 @@ include ':instrumentation:netty:netty-4.1'
include ':instrumentation:okhttp:okhttp-2.2'
include ':instrumentation:okhttp:okhttp-3.0'
include ':instrumentation:opentelemetry-api-beta'
include ':instrumentation:oshi:library'
include ':instrumentation:play:play-2.3'
include ':instrumentation:play:play-2.4'
include ':instrumentation:play:play-2.6'