Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.instrumentation.runtimemetrics.java17.internal;

import java.util.regex.Pattern;
import javax.annotation.Nullable;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedThread;
Expand All @@ -14,13 +15,17 @@
* any time.
*/
public final class ThreadGrouper {
private static final Pattern SIMILAR_THREAD_NAME_PATTERN = Pattern.compile("\\d+");

// FIXME doesn't actually do any grouping, but should be safe for now
// FIXME only handles substrings of contiguous digits -> a single `x`, but should be good
// enough for now
@Nullable
public String groupedName(RecordedEvent ev) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used anywhere now that AbstractThreadDispatchingHandler was deleted?

Copy link
Contributor Author

@jhayes2-chwy jhayes2-chwy Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, addressed.

Object thisField = ev.getValue("eventThread");
if (thisField instanceof RecordedThread) {
return ((RecordedThread) thisField).getJavaName();
return SIMILAR_THREAD_NAME_PATTERN
.matcher(((RecordedThread) thisField).getJavaName())
.replaceAll("x");
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,38 @@
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.AbstractThreadDispatchingHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.DurationUtil;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RecordedEventHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.ThreadGrouper;
import java.time.Duration;
import java.util.Optional;
import java.util.function.Consumer;
import jdk.jfr.consumer.RecordedEvent;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class LongLockHandler extends AbstractThreadDispatchingHandler {
public final class LongLockHandler implements RecordedEventHandler {
private static final String METRIC_NAME = "jvm.cpu.longlock";
private static final String METRIC_DESCRIPTION = "Long lock times";
private static final String EVENT_NAME = "jdk.JavaMonitorWait";

private static final String EVENT_THREAD = "eventThread";

private final DoubleHistogram histogram;
private final Attributes attributes;

public LongLockHandler(Meter meter, ThreadGrouper grouper) {
super(grouper);
super();
histogram =
meter
.histogramBuilder(METRIC_NAME)
.setDescription(METRIC_DESCRIPTION)
.setUnit(Constants.SECONDS)
.build();

attributes = Attributes.empty();
}

@Override
Expand All @@ -50,34 +54,17 @@ public JfrFeature getFeature() {
}

@Override
public Consumer<RecordedEvent> createPerThreadSummarizer(String threadName) {
return new PerThreadLongLockHandler(histogram, threadName);
public void accept(RecordedEvent recordedEvent) {
if (recordedEvent.hasField(EVENT_THREAD)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check needed? I think that previously this check was always true because ThreadGrouper would have returned null if the thread was missing. I'd remove this check unless anybody sees a reason why to keep it.

Copy link
Contributor Author

@jhayes2-chwy jhayes2-chwy Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it may have been needed in order to add an attribute for the monitor's className, but I'd suspect that would have the same high-cardinality concerns as threadName, presuming it was used on Metrics.

Though I can personally see where that could provide a lot of value without cardinality issues, e.g. as an attribute on a Span Event or Profile. But unless there's active plans to build out that logic super soon, IMHO it'd be a premature optimization to keep the check in, at least for this specific reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have gone ahead and removed it.

histogram.record(DurationUtil.toSeconds(recordedEvent.getDuration()), attributes);
}
// What about the class name in MONITOR_CLASS ?
// We can get a stack trace from the thread on the event
// var eventThread = recordedEvent.getThread(EVENT_THREAD);
}

@Override
public Optional<Duration> getThreshold() {
return Optional.empty();
}

private static class PerThreadLongLockHandler implements Consumer<RecordedEvent> {
private static final String EVENT_THREAD = "eventThread";

private final DoubleHistogram histogram;
private final Attributes attributes;

public PerThreadLongLockHandler(DoubleHistogram histogram, String threadName) {
this.histogram = histogram;
this.attributes = Attributes.of(Constants.ATTR_THREAD_NAME, threadName);
}

@Override
public void accept(RecordedEvent recordedEvent) {
if (recordedEvent.hasField(EVENT_THREAD)) {
histogram.record(DurationUtil.toSeconds(recordedEvent.getDuration()), attributes);
}
// What about the class name in MONITOR_CLASS ?
// We can get a stack trace from the thread on the event
// var eventThread = recordedEvent.getThread(EVENT_THREAD);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.AbstractThreadDispatchingHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RecordedEventHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.ThreadGrouper;
import java.util.function.Consumer;
import jdk.jfr.consumer.RecordedEvent;

/**
Expand All @@ -22,20 +21,23 @@
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class ObjectAllocationInNewTlabHandler extends AbstractThreadDispatchingHandler {
public final class ObjectAllocationInNewTlabHandler implements RecordedEventHandler {
private static final String EVENT_NAME = "jdk.ObjectAllocationInNewTLAB";
private static final String TLAB_SIZE = "tlabSize";

private final LongHistogram histogram;
private final Attributes attributes;

public ObjectAllocationInNewTlabHandler(Meter meter, ThreadGrouper grouper) {
super(grouper);
super();
histogram =
meter
.histogramBuilder(Constants.METRIC_NAME_MEMORY_ALLOCATION)
.setDescription(Constants.METRIC_DESCRIPTION_MEMORY_ALLOCATION)
.setUnit(Constants.BYTES)
.ofLongs()
.build();
attributes = Attributes.of(Constants.ATTR_ARENA_NAME, "TLAB");
}

@Override
Expand All @@ -49,29 +51,9 @@ public JfrFeature getFeature() {
}

@Override
public Consumer<RecordedEvent> createPerThreadSummarizer(String threadName) {
return new PerThreadObjectAllocationInNewTlabHandler(histogram, threadName);
}

/** This class aggregates all TLAB allocation JFR events for a single thread */
private static class PerThreadObjectAllocationInNewTlabHandler
implements Consumer<RecordedEvent> {
private static final String TLAB_SIZE = "tlabSize";

private final LongHistogram histogram;
private final Attributes attributes;

public PerThreadObjectAllocationInNewTlabHandler(LongHistogram histogram, String threadName) {
this.histogram = histogram;
this.attributes =
Attributes.of(Constants.ATTR_THREAD_NAME, threadName, Constants.ATTR_ARENA_NAME, "TLAB");
}

@Override
public void accept(RecordedEvent ev) {
histogram.record(ev.getLong(TLAB_SIZE), attributes);
// Probably too high a cardinality
// ev.getClass("objectClass").getName();
}
public void accept(RecordedEvent ev) {
histogram.record(ev.getLong(TLAB_SIZE), attributes);
// Probably too high a cardinality
// ev.getClass("objectClass").getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.AbstractThreadDispatchingHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RecordedEventHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.ThreadGrouper;
import java.util.function.Consumer;
import jdk.jfr.consumer.RecordedEvent;

/**
Expand All @@ -22,20 +21,24 @@
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class ObjectAllocationOutsideTlabHandler extends AbstractThreadDispatchingHandler {
public final class ObjectAllocationOutsideTlabHandler implements RecordedEventHandler {
private static final String EVENT_NAME = "jdk.ObjectAllocationOutsideTLAB";
private static final String ALLOCATION_SIZE = "allocationSize";

private final LongHistogram histogram;
private final Attributes attributes;

public ObjectAllocationOutsideTlabHandler(Meter meter, ThreadGrouper grouper) {
super(grouper);
super();
histogram =
meter
.histogramBuilder(Constants.METRIC_NAME_MEMORY_ALLOCATION)
.setDescription(Constants.METRIC_DESCRIPTION_MEMORY_ALLOCATION)
.setUnit(Constants.BYTES)
.ofLongs()
.build();

attributes = Attributes.of(Constants.ATTR_ARENA_NAME, "Main");
}

@Override
Expand All @@ -49,29 +52,9 @@ public JfrFeature getFeature() {
}

@Override
public Consumer<RecordedEvent> createPerThreadSummarizer(String threadName) {
return new PerThreadObjectAllocationOutsideTlabHandler(histogram, threadName);
}

/** This class aggregates all non-TLAB allocation JFR events for a single thread */
private static class PerThreadObjectAllocationOutsideTlabHandler
implements Consumer<RecordedEvent> {
private static final String ALLOCATION_SIZE = "allocationSize";

private final LongHistogram histogram;
private final Attributes attributes;

public PerThreadObjectAllocationOutsideTlabHandler(LongHistogram histogram, String threadName) {
this.histogram = histogram;
this.attributes =
Attributes.of(Constants.ATTR_THREAD_NAME, threadName, Constants.ATTR_ARENA_NAME, "Main");
}

@Override
public void accept(RecordedEvent ev) {
histogram.record(ev.getLong(ALLOCATION_SIZE), attributes);
// Probably too high a cardinality
// ev.getClass("objectClass").getName();
}
public void accept(RecordedEvent ev) {
histogram.record(ev.getLong(ALLOCATION_SIZE), attributes);
// Probably too high a cardinality
// ev.getClass("objectClass").getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.instrumentation.runtimemetrics.java17.JfrFeature;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.AbstractThreadDispatchingHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.Constants;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.DurationUtil;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.RecordedEventHandler;
import io.opentelemetry.instrumentation.runtimemetrics.java17.internal.ThreadGrouper;
import java.util.function.Consumer;
import jdk.jfr.consumer.RecordedEvent;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class NetworkReadHandler extends AbstractThreadDispatchingHandler {
public final class NetworkReadHandler implements RecordedEventHandler {
private static final String EVENT_NAME = "jdk.SocketRead";
private static final String BYTES_READ = "bytesRead";

private final LongHistogram bytesHistogram;
private final DoubleHistogram durationHistogram;
private final Attributes attributes;

public NetworkReadHandler(Meter meter, ThreadGrouper nameNormalizer) {
super(nameNormalizer);
super();
bytesHistogram =
meter
.histogramBuilder(Constants.METRIC_NAME_NETWORK_BYTES)
Expand All @@ -42,6 +43,7 @@ public NetworkReadHandler(Meter meter, ThreadGrouper nameNormalizer) {
.setDescription(Constants.METRIC_DESCRIPTION_NETWORK_DURATION)
.setUnit(Constants.SECONDS)
.build();
attributes = Attributes.of(Constants.ATTR_NETWORK_MODE, Constants.NETWORK_MODE_READ);
}

@Override
Expand All @@ -55,33 +57,8 @@ public JfrFeature getFeature() {
}

@Override
public Consumer<RecordedEvent> createPerThreadSummarizer(String threadName) {
return new PerThreadNetworkReadHandler(bytesHistogram, durationHistogram, threadName);
}

private static class PerThreadNetworkReadHandler implements Consumer<RecordedEvent> {
private static final String BYTES_READ = "bytesRead";

private final LongHistogram bytesHistogram;
private final DoubleHistogram durationHistogram;
private final Attributes attributes;

public PerThreadNetworkReadHandler(
LongHistogram bytesHistogram, DoubleHistogram durationHistogram, String threadName) {
this.bytesHistogram = bytesHistogram;
this.durationHistogram = durationHistogram;
this.attributes =
Attributes.of(
Constants.ATTR_THREAD_NAME,
threadName,
Constants.ATTR_NETWORK_MODE,
Constants.NETWORK_MODE_READ);
}

@Override
public void accept(RecordedEvent ev) {
bytesHistogram.record(ev.getLong(BYTES_READ), attributes);
durationHistogram.record(DurationUtil.toSeconds(ev.getDuration()), attributes);
}
public void accept(RecordedEvent ev) {
bytesHistogram.record(ev.getLong(BYTES_READ), attributes);
durationHistogram.record(DurationUtil.toSeconds(ev.getDuration()), attributes);
}
}
Loading
Loading