Skip to content

Commit cd2c935

Browse files
authored
Tidy up jfr-streaming (#127)
* Tidy up jfr-streaming * Network read and write use same instruments * Restore init method and call with Meter
1 parent 08ecf83 commit cd2c935

21 files changed

+401
-468
lines changed

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/HandlerRegistry.java

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@
1919
import io.opentelemetry.contrib.jfr.metrics.internal.network.NetworkReadHandler;
2020
import io.opentelemetry.contrib.jfr.metrics.internal.network.NetworkWriteHandler;
2121
import java.util.*;
22-
import java.util.stream.Stream;
2322

2423
final class HandlerRegistry {
25-
private static final String SCHEMA_URL = "https://opentelemetry.io/schemas/1.6.1";
2624
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.contrib.jfr";
2725
private static final String INSTRUMENTATION_VERSION = "1.7.0-SNAPSHOT";
2826

@@ -33,28 +31,31 @@ private HandlerRegistry(List<? extends RecordedEventHandler> mappers) {
3331
}
3432

3533
static HandlerRegistry createDefault(MeterProvider meterProvider) {
36-
var otelMeter = meterProvider.get(INSTRUMENTATION_NAME, INSTRUMENTATION_VERSION, null);
37-
34+
var meter =
35+
meterProvider
36+
.meterBuilder(INSTRUMENTATION_NAME)
37+
.setInstrumentationVersion(INSTRUMENTATION_VERSION)
38+
.build();
3839
var grouper = new ThreadGrouper();
39-
var filtered =
40+
var handlers =
4041
List.of(
41-
new ObjectAllocationInNewTLABHandler(otelMeter, grouper),
42-
new ObjectAllocationOutsideTLABHandler(otelMeter, grouper),
43-
new NetworkReadHandler(otelMeter, grouper),
44-
new NetworkWriteHandler(otelMeter, grouper),
45-
new G1GarbageCollectionHandler(otelMeter),
46-
new GCHeapSummaryHandler(otelMeter),
47-
new ContextSwitchRateHandler(otelMeter),
48-
new OverallCPULoadHandler(otelMeter),
49-
new ContainerConfigurationHandler(otelMeter),
50-
new LongLockHandler(otelMeter, grouper));
51-
filtered.forEach(RecordedEventHandler::init);
52-
53-
return new HandlerRegistry(filtered);
42+
new ObjectAllocationInNewTLABHandler(grouper),
43+
new ObjectAllocationOutsideTLABHandler(grouper),
44+
new NetworkReadHandler(grouper),
45+
new NetworkWriteHandler(grouper),
46+
new G1GarbageCollectionHandler(),
47+
new GCHeapSummaryHandler(),
48+
new ContextSwitchRateHandler(),
49+
new OverallCPULoadHandler(),
50+
new ContainerConfigurationHandler(),
51+
new LongLockHandler(grouper));
52+
handlers.forEach(handler -> handler.initializeMeter(meter));
53+
54+
return new HandlerRegistry(handlers);
5455
}
5556

56-
/** @return a stream of all entries in this registry. */
57-
Stream<RecordedEventHandler> all() {
58-
return mappers.stream();
57+
/** @return all entries in this registry. */
58+
List<RecordedEventHandler> all() {
59+
return mappers;
5960
}
6061
}

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/JfrMetrics.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
import io.opentelemetry.api.metrics.MeterProvider;
99
import io.opentelemetry.contrib.jfr.metrics.internal.RecordedEventHandler;
1010
import java.util.concurrent.Executors;
11-
import java.util.function.Consumer;
1211
import java.util.logging.Level;
1312
import java.util.logging.Logger;
14-
import jdk.jfr.EventSettings;
1513
import jdk.jfr.consumer.RecordingStream;
1614

1715
/** The entry point class for the JFR-over-OpenTelemetry support. */
@@ -34,21 +32,18 @@ public static void enable(MeterProvider meterProvider) {
3432
jfrMonitorService.submit(
3533
() -> {
3634
try (var recordingStream = new RecordingStream()) {
37-
var enableMappedEvent = eventEnablerFor(recordingStream);
38-
toMetricRegistry.all().forEach(enableMappedEvent);
35+
toMetricRegistry.all().forEach(handler -> enableHandler(recordingStream, handler));
3936
recordingStream.setReuse(false);
4037
logger.log(Level.FINE, "Starting recording stream...");
4138
recordingStream.start(); // run forever
4239
}
4340
});
4441
}
4542

46-
private static Consumer<RecordedEventHandler> eventEnablerFor(RecordingStream recordingStream) {
47-
return handler -> {
48-
EventSettings eventSettings = recordingStream.enable(handler.getEventName());
49-
handler.getPollingDuration().ifPresent(eventSettings::withPeriod);
50-
handler.getThreshold().ifPresent(eventSettings::withThreshold);
51-
recordingStream.onEvent(handler.getEventName(), handler);
52-
};
43+
private static void enableHandler(RecordingStream recordingStream, RecordedEventHandler handler) {
44+
var eventSettings = recordingStream.enable(handler.getEventName());
45+
handler.getPollingDuration().ifPresent(eventSettings::withPeriod);
46+
handler.getThreshold().ifPresent(eventSettings::withThreshold);
47+
recordingStream.onEvent(handler.getEventName(), handler);
5348
}
5449
}

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/AbstractThreadDispatchingHandler.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,30 @@
77

88
import java.util.HashMap;
99
import java.util.Map;
10-
import java.util.Optional;
10+
import java.util.function.Consumer;
1111
import jdk.jfr.consumer.RecordedEvent;
1212

1313
public abstract class AbstractThreadDispatchingHandler implements RecordedEventHandler {
1414
// Will need pruning code for fast-cycling thread frameworks to prevent memory leaks
15-
protected final Map<String, RecordedEventHandler> perThread = new HashMap<>();
16-
protected final ThreadGrouper grouper;
15+
private final Map<String, Consumer<RecordedEvent>> perThread = new HashMap<>();
16+
private final ThreadGrouper grouper;
1717

1818
public AbstractThreadDispatchingHandler(ThreadGrouper grouper) {
1919
this.grouper = grouper;
2020
}
2121

22-
public void reset() {
23-
perThread.clear();
24-
}
25-
2622
public abstract String getEventName();
2723

28-
public abstract RecordedEventHandler createPerThreadSummarizer(String threadName);
24+
public abstract Consumer<RecordedEvent> createPerThreadSummarizer(String threadName);
2925

3026
@Override
3127
public void accept(RecordedEvent ev) {
32-
final Optional<String> possibleGroupedThreadName = grouper.groupedName(ev);
33-
possibleGroupedThreadName.ifPresent(
34-
groupedThreadName -> {
35-
perThread.computeIfAbsent(groupedThreadName, name -> createPerThreadSummarizer(name));
36-
perThread.get(groupedThreadName).accept(ev);
37-
});
28+
grouper
29+
.groupedName(ev)
30+
.ifPresent(
31+
groupedThreadName ->
32+
perThread
33+
.computeIfAbsent(groupedThreadName, this::createPerThreadSummarizer)
34+
.accept(ev));
3835
}
3936
}

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/Constants.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@ private Constants() {}
1515
public static final String BYTES = "B";
1616
public static final String MILLISECONDS = "ms";
1717
public static final String PERCENTAGE = "%age";
18-
public static final String READ = "read";
19-
public static final String WRITE = "write";
2018
public static final String USER = "user";
2119
public static final String SYSTEM = "system";
2220
public static final String MACHINE = "machine.total";
2321
public static final String G1 = "g1";
2422
public static final String USED = "used";
2523
public static final String COMMITTED = "committed";
2624

25+
public static final String NETWORK_BYTES_NAME = "runtime.jvm.network.io";
26+
public static final String NETWORK_BYTES_DESCRIPTION = "Network read/write bytes";
27+
public static final String NETWORK_DURATION_NAME = "runtime.jvm.network.duration";
28+
public static final String NETWORK_DURATION_DESCRIPTION = "Network read/write duration";
29+
public static final String NETWORK_MODE_READ = "read";
30+
public static final String NETWORK_MODE_WRITE = "write";
31+
2732
public static final AttributeKey<String> ATTR_THREAD_NAME = AttributeKey.stringKey("thread.name");
2833
public static final AttributeKey<String> ATTR_ARENA_NAME = AttributeKey.stringKey("arena");
2934
public static final AttributeKey<String> ATTR_NETWORK_MODE = AttributeKey.stringKey("mode");

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/RecordedEventHandler.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package io.opentelemetry.contrib.jfr.metrics.internal;
77

8+
import io.opentelemetry.api.metrics.Meter;
9+
import io.opentelemetry.api.metrics.internal.NoopMeter;
810
import java.time.Duration;
911
import java.util.Optional;
1012
import java.util.function.Consumer;
@@ -31,6 +33,14 @@ default boolean test(RecordedEvent event) {
3133
return event.getEventType().getName().equalsIgnoreCase(getEventName());
3234
}
3335

36+
/**
37+
* Set the OpenTelemetry {@link Meter} after the SDK has been initialized. Until called,
38+
* implementations should use instruments from {@link NoopMeter}.
39+
*
40+
* @param meter the meter
41+
*/
42+
void initializeMeter(Meter meter);
43+
3444
/**
3545
* Optionally returns a polling duration for JFR events, if present
3646
*
@@ -50,13 +60,4 @@ default Optional<Duration> getPollingDuration() {
5060
default Optional<Duration> getThreshold() {
5161
return Optional.empty();
5262
}
53-
54-
/**
55-
* Initialize the handler. Default implementation is a no-op
56-
*
57-
* @return
58-
*/
59-
default RecordedEventHandler init() {
60-
return this;
61-
}
6263
}

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/ThreadGrouper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
import jdk.jfr.consumer.RecordedEvent;
1010
import jdk.jfr.consumer.RecordedThread;
1111

12-
public class ThreadGrouper {
12+
public final class ThreadGrouper {
1313
// FIXME doesn't actually do any grouping, but should be safe for now
1414
public Optional<String> groupedName(RecordedEvent ev) {
1515
Object thisField = ev.getValue("eventThread");
16-
if (thisField != null && thisField instanceof RecordedThread) {
16+
if (thisField instanceof RecordedThread) {
1717
return Optional.of(((RecordedThread) thisField).getJavaName());
1818
}
1919
return Optional.empty();

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/container/ContainerConfigurationHandler.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.ONE;
99

1010
import io.opentelemetry.api.metrics.Meter;
11+
import io.opentelemetry.api.metrics.internal.NoopMeter;
1112
import io.opentelemetry.contrib.jfr.metrics.internal.RecordedEventHandler;
1213
import jdk.jfr.consumer.RecordedEvent;
1314

@@ -17,21 +18,19 @@ public final class ContainerConfigurationHandler implements RecordedEventHandler
1718

1819
private static final String EFFECTIVE_CPU_COUNT = "effectiveCpuCount";
1920

20-
private final Meter otelMeter;
2121
private volatile long value = 0L;
2222

23-
public ContainerConfigurationHandler(Meter otelMeter) {
24-
this.otelMeter = otelMeter;
23+
public ContainerConfigurationHandler() {
24+
initializeMeter(NoopMeter.getInstance());
2525
}
2626

27-
public ContainerConfigurationHandler init() {
28-
otelMeter
27+
@Override
28+
public void initializeMeter(Meter meter) {
29+
meter
2930
.upDownCounterBuilder(METRIC_NAME)
3031
.ofDoubles()
3132
.setUnit(ONE)
3233
.buildWithCallback(codm -> codm.observe(value));
33-
34-
return this;
3534
}
3635

3736
@Override

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/cpu/ContextSwitchRateHandler.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.HERTZ;
99

1010
import io.opentelemetry.api.metrics.*;
11+
import io.opentelemetry.api.metrics.internal.NoopMeter;
1112
import io.opentelemetry.contrib.jfr.metrics.internal.RecordedEventHandler;
1213
import java.time.Duration;
1314
import java.util.Optional;
@@ -17,20 +18,10 @@ public final class ContextSwitchRateHandler implements RecordedEventHandler {
1718
private static final String EVENT_NAME = "jdk.ThreadContextSwitchRate";
1819
private static final String METRIC_NAME = "runtime.jvm.cpu.context_switch";
1920

20-
private final Meter otelMeter;
2121
private volatile double value = 0;
2222

23-
public ContextSwitchRateHandler(Meter otelMeter) {
24-
this.otelMeter = otelMeter;
25-
}
26-
27-
public ContextSwitchRateHandler init() {
28-
otelMeter
29-
.upDownCounterBuilder(METRIC_NAME)
30-
.ofDoubles()
31-
.setUnit(HERTZ)
32-
.buildWithCallback(codm -> codm.observe(value));
33-
return this;
23+
public ContextSwitchRateHandler() {
24+
initializeMeter(NoopMeter.getInstance());
3425
}
3526

3627
@Override
@@ -42,6 +33,15 @@ public String getEventName() {
4233
return EVENT_NAME;
4334
}
4435

36+
@Override
37+
public void initializeMeter(Meter meter) {
38+
meter
39+
.upDownCounterBuilder(METRIC_NAME)
40+
.ofDoubles()
41+
.setUnit(HERTZ)
42+
.buildWithCallback(codm -> codm.observe(value));
43+
}
44+
4545
@Override
4646
public Optional<Duration> getPollingDuration() {
4747
return Optional.of(Duration.ofSeconds(1));

jfr-streaming/src/main/java/io/opentelemetry/contrib/jfr/metrics/internal/cpu/LongLockHandler.java

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,38 @@
88
import static io.opentelemetry.contrib.jfr.metrics.internal.Constants.ATTR_THREAD_NAME;
99

1010
import io.opentelemetry.api.common.Attributes;
11+
import io.opentelemetry.api.metrics.BoundDoubleHistogram;
12+
import io.opentelemetry.api.metrics.DoubleHistogram;
1113
import io.opentelemetry.api.metrics.Meter;
14+
import io.opentelemetry.api.metrics.internal.NoopMeter;
1215
import io.opentelemetry.contrib.jfr.metrics.internal.AbstractThreadDispatchingHandler;
1316
import io.opentelemetry.contrib.jfr.metrics.internal.Constants;
14-
import io.opentelemetry.contrib.jfr.metrics.internal.RecordedEventHandler;
1517
import io.opentelemetry.contrib.jfr.metrics.internal.ThreadGrouper;
1618
import java.time.Duration;
1719
import java.util.Optional;
20+
import java.util.function.Consumer;
21+
import jdk.jfr.consumer.RecordedEvent;
1822

1923
public final class LongLockHandler extends AbstractThreadDispatchingHandler {
20-
static final String EVENT_NAME = "jdk.JavaMonitorWait";
21-
private final Meter otelMeter;
24+
private static final String EVENT_NAME = "jdk.JavaMonitorWait";
2225
private static final String METRIC_NAME = "runtime.jvm.cpu.longlock.time";
2326
private static final String DESCRIPTION = "Long lock times";
2427

25-
public LongLockHandler(Meter otelMeter, ThreadGrouper grouper) {
28+
private DoubleHistogram histogram;
29+
30+
public LongLockHandler(ThreadGrouper grouper) {
2631
super(grouper);
27-
this.otelMeter = otelMeter;
32+
initializeMeter(NoopMeter.getInstance());
33+
}
34+
35+
@Override
36+
public void initializeMeter(Meter meter) {
37+
histogram =
38+
meter
39+
.histogramBuilder(METRIC_NAME)
40+
.setDescription(DESCRIPTION)
41+
.setUnit(Constants.MILLISECONDS)
42+
.build();
2843
}
2944

3045
@Override
@@ -33,18 +48,32 @@ public String getEventName() {
3348
}
3449

3550
@Override
36-
public RecordedEventHandler createPerThreadSummarizer(String threadName) {
37-
var attr = Attributes.of(ATTR_THREAD_NAME, threadName);
38-
var builder = otelMeter.histogramBuilder(METRIC_NAME);
39-
builder.setDescription(DESCRIPTION);
40-
builder.setUnit(Constants.MILLISECONDS);
41-
var histogram = builder.build().bind(attr);
42-
var ret = new PerThreadLongLockHandler(histogram, threadName);
43-
return ret.init();
51+
public Consumer<RecordedEvent> createPerThreadSummarizer(String threadName) {
52+
return new PerThreadLongLockHandler(histogram, threadName);
4453
}
4554

4655
@Override
4756
public Optional<Duration> getThreshold() {
4857
return Optional.empty();
4958
}
59+
60+
private static class PerThreadLongLockHandler implements Consumer<RecordedEvent> {
61+
private static final String EVENT_THREAD = "eventThread";
62+
63+
private final BoundDoubleHistogram boundHistogram;
64+
65+
public PerThreadLongLockHandler(DoubleHistogram histogram, String threadName) {
66+
this.boundHistogram = histogram.bind(Attributes.of(ATTR_THREAD_NAME, threadName));
67+
}
68+
69+
@Override
70+
public void accept(RecordedEvent recordedEvent) {
71+
if (recordedEvent.hasField(EVENT_THREAD)) {
72+
boundHistogram.record(recordedEvent.getDuration().toMillis());
73+
}
74+
// What about the class name in MONITOR_CLASS ?
75+
// We can get a stack trace from the thread on the event
76+
// var eventThread = recordedEvent.getThread(EVENT_THREAD);
77+
}
78+
}
5079
}

0 commit comments

Comments
 (0)