Skip to content
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

Update CPU handler #651

Closed
Closed
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 @@ -26,37 +26,54 @@ private Constants() {}
public static final String REGION_COUNT = "region.count";
public static final String COMMITTED = "committed";
public static final String RESERVED = "reserved";
public static final String INITIAL_SIZE = "initialSize";
public static final String USED = "used";
public static final String COMMITTED_SIZE = "committedSize";
public static final String RESERVED_SIZE = "reservedSize";

public static final String DAEMON = "daemon";
public static final String HEAP = "heap";
public static final String NON_HEAP = "nonheap";
public static final String NETWORK_MODE_READ = "read";
public static final String NETWORK_MODE_WRITE = "write";
public static final String DURATION = "duration";
public static final String END_OF_MINOR_GC = "end of minor GC";
public static final String END_OF_MAJOR_GC = "end of major GC";

public static final String METRIC_NAME_NETWORK_BYTES = "process.runtime.jvm.network.io";
public static final String METRIC_DESCRIPTION_NETWORK_BYTES = "Network read/write bytes";
public static final String METRIC_NAME_NETWORK_DURATION = "process.runtime.jvm.network.time";
public static final String METRIC_DESCRIPTION_NETWORK_DURATION = "Network read/write duration";
public static final String METRIC_NAME_COMMITTED = "process.runtime.jvm.memory.committed";
public static final String METRIC_DESCRIPTION_COMMITTED = "Measure of memory committed";
public static final String NETWORK_MODE_READ = "read";
public static final String NETWORK_MODE_WRITE = "write";
public static final String METRIC_NAME_MEMORY = "process.runtime.jvm.memory.usage";
public static final String METRIC_DESCRIPTION_MEMORY = "Measure of memory used";
public static final String METRIC_NAME_MEMORY_AFTER =
"process.runtime.jvm.memory.usage_after_last_gc";
public static final String METRIC_DESCRIPTION_MEMORY = "Measure of memory used";
public static final String METRIC_DESCRIPTION_MEMORY_AFTER =
"Measure of memory used, as measured after the most recent garbage collection event on this pool";
public static final String METRIC_NAME_MEMORY_ALLOCATION =
"process.runtime.jvm.memory.allocation";
public static final String METRIC_DESCRIPTION_MEMORY_ALLOCATION = "Allocation";
public static final String METRIC_NAME_MEMORY_INIT = "process.runtime.jvm.memory.init";
public static final String METRIC_DESCRIPTION_MEMORY_INIT = "Measure of initial memory requested";
public static final String METRIC_NAME_MEMORY_LIMIT = "process.runtime.jvm.memory.limit";
public static final String METRIC_DESCRIPTION_MEMORY_LIMIT = "Measure of max obtainable memory";
public static final String METRIC_NAME_GC_DURATION = "process.runtime.jvm.gc.duration";
public static final String METRIC_DESCRIPTION_GC_DURATION =
"Duration of JVM garbage collection actions";

public static final AttributeKey<String> ATTR_THREAD_NAME = AttributeKey.stringKey("thread.name");
public static final AttributeKey<String> ATTR_ARENA_NAME = AttributeKey.stringKey("arena");
public static final AttributeKey<String> ATTR_NETWORK_MODE = AttributeKey.stringKey("mode");
public static final AttributeKey<String> ATTR_TYPE = AttributeKey.stringKey("type");
public static final AttributeKey<String> ATTR_POOL = AttributeKey.stringKey("pool");
public static final AttributeKey<String> ATTR_GC = AttributeKey.stringKey("pool");
public static final AttributeKey<String> ATTR_ACTION = AttributeKey.stringKey("action");
public static final AttributeKey<Boolean> ATTR_DAEMON = AttributeKey.booleanKey(DAEMON);
public static final String UNIT_CLASSES = "{classes}";
public static final String UNIT_THREADS = "{threads}";
public static final String UNIT_BUFFERS = "{buffers}";
public static final String UNIT_UTILIZATION = "1";
public static final int SECONDS_PER_MIN = 60;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,37 @@

package io.opentelemetry.contrib.jfr.metrics.internal.cpu;

import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.SECONDS_PER_MIN;
import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.UNIT_UTILIZATION;
import static io.opentelemetry.contrib.jfr.metrics.internal.RecordedEventHandler.defaultMeter;

import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.contrib.jfr.metrics.internal.RecordedEventHandler;
import java.time.Duration;
import java.util.ArrayDeque;
import java.util.Optional;
import jdk.jfr.consumer.RecordedEvent;

public final class OverallCPULoadHandler implements RecordedEventHandler {
private static final String METRIC_NAME_PROCESS = "process.runtime.jvm.cpu.utilization";
private static final String METRIC_NAME_MACHINE = "process.runtime.jvm.system.cpu.utilization";
private static final String METRIC_NAME_MACHINE_MINUTE = "process.runtime.jvm.system.cpu.load_1m";
private static final String METRIC_DESCRIPTION_PROCESS = "Recent CPU utilization for the process";
private static final String METRIC_DESCRIPTION_MACHINE =
"Recent CPU utilization for the whole system";
private static final String METRIC_DESCRIPTION_MACHINE_MINUTE =
"Average CPU load of the whole system for the last minute";

private static final String EVENT_NAME = "jdk.CPULoad";
private static final String JVM_USER = "jvmUser";
private static final String JVM_SYSTEM = "jvmSystem";
private static final String MACHINE_TOTAL = "machineTotal";
private volatile double process = 0;
private volatile double machine = 0;
private volatile double machineMinuteAverage = 0;
private volatile double machineSum = 0;

private final ArrayDeque<Double> machineMinuteQueue = new ArrayDeque<>();

public OverallCPULoadHandler() {
initializeMeter(defaultMeter());
Expand All @@ -38,28 +47,42 @@ public void initializeMeter(Meter meter) {
.gaugeBuilder(METRIC_NAME_PROCESS)
.setDescription(METRIC_DESCRIPTION_PROCESS)
.setUnit(UNIT_UTILIZATION)
.buildWithCallback(
measurement -> {
measurement.record(process);
});
.buildWithCallback(measurement -> measurement.record(process));
meter
.gaugeBuilder(METRIC_NAME_MACHINE)
.setDescription(METRIC_DESCRIPTION_MACHINE)
.setUnit(UNIT_UTILIZATION)
.buildWithCallback(
measurement -> {
measurement.record(machine);
});
.buildWithCallback(measurement -> measurement.record(machine));
meter
.gaugeBuilder(METRIC_NAME_MACHINE_MINUTE)
.setDescription(METRIC_DESCRIPTION_MACHINE_MINUTE)
.setUnit(UNIT_UTILIZATION)
.buildWithCallback(measurement -> measurement.record(machineMinuteAverage));
}

public synchronized double updateMovingAverage(double newDataPoint) {
// Synchronized to avoid races on machineSum
if (machineMinuteQueue.size() == SECONDS_PER_MIN) {
machineSum -= machineMinuteQueue.poll();
}
// Add new data point
machineSum += newDataPoint;
machineMinuteQueue.add(newDataPoint);
// Compute new result
return machineSum / machineMinuteQueue.size();
}

@Override
public void accept(RecordedEvent ev) {
if (ev.hasField(JVM_USER) && ev.hasField(JVM_SYSTEM)) {
process = ev.getDouble(JVM_USER) + ev.getDouble(JVM_SYSTEM);
}
if (ev.hasField(MACHINE_TOTAL)) {
machine = ev.getDouble(MACHINE_TOTAL);
}

machineMinuteAverage = updateMovingAverage(machine);

if (ev.hasField(JVM_USER) && ev.hasField(JVM_SYSTEM)) {
process = ev.getDouble(JVM_USER) + ev.getDouble(JVM_SYSTEM);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
package io.opentelemetry.contrib.jfr.metrics;

import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.MILLISECONDS;
import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.SECONDS_PER_MIN;
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.opentelemetry.contrib.jfr.metrics.internal.cpu.OverallCPULoadHandler;
import org.junit.jupiter.api.Test;

public class JfrCPULockTest extends AbstractMetricsTest {

@Test
public void shouldHaveLockEvents() throws Exception {
// This should generate some events
Expand All @@ -26,4 +28,20 @@ public void shouldHaveLockEvents() throws Exception {
.hasUnit(MILLISECONDS)
.hasHistogramSatisfying(histogram -> {}));
}

@Test
public void testMovingAverage() {
OverallCPULoadHandler handler = new OverallCPULoadHandler();
double sum = 0;
for (int i = 0; i < 2 * SECONDS_PER_MIN; i++) {
double result = handler.updateMovingAverage(i);
sum += i;
double divisor = i + 1;
if (i >= SECONDS_PER_MIN) {
sum -= (i - SECONDS_PER_MIN);
divisor = SECONDS_PER_MIN;
}
assertTrue(result == sum / divisor);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.jfr.metrics;

import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.UNIT_UTILIZATION;
import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

public class OverallCPULoadHandlerTest extends AbstractMetricsTest {

private void check(String name, String description) {
waitAndAssertMetrics(
metric ->
metric
.hasName(name)
.hasUnit(UNIT_UTILIZATION)
.hasDescription(description)
.hasDoubleGaugeSatisfying(
gauge ->
gauge.hasPointsSatisfying(
point ->
point.satisfies(
pointData -> {
assertThat(pointData.getValue()).isGreaterThan(0);
assertThat(pointData.getValue()).isLessThan(1.0);
}))));
}

@Test
public void shouldHaveCPULoadEvents() {
check(
"process.runtime.jvm.system.cpu.load_1m",
"Average CPU load of the whole system for the last minute");
check(
"process.runtime.jvm.system.cpu.utilization",
"Recent CPU utilization for the whole system");
check("process.runtime.jvm.cpu.utilization", "Recent CPU utilization for the process");
}
}