Skip to content
This repository was archived by the owner on Jan 14, 2026. It is now read-only.

Commit 14c5739

Browse files
authored
Merge pull request #5 from zegelin/jvm-metrics
Adds support for JVM metrics, additional C* metrics + performance improvements
2 parents b41616c + fc2b2b3 commit 14c5739

45 files changed

Lines changed: 1551 additions & 419 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

agent/src/main/java/com/zegelin/agent/AgentArgumentParser.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.google.common.collect.ImmutableList;
44

5-
import java.util.ArrayList;
65
import java.util.Deque;
76
import java.util.LinkedList;
87
import java.util.List;

agent/src/main/java/com/zegelin/prometheus/cassandra/Agent.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
import com.zegelin.agent.AgentArgumentParser;
55
import com.zegelin.prometheus.cassandra.cli.HarvesterOptions;
66
import com.zegelin.prometheus.cli.HttpServerOptions;
7-
87
import com.zegelin.prometheus.netty.Server;
98
import picocli.CommandLine;
109
import picocli.CommandLine.Command;
1110
import picocli.CommandLine.Mixin;
1211

13-
import javax.management.*;
12+
import javax.management.InstanceAlreadyExistsException;
13+
import javax.management.MBeanRegistrationException;
14+
import javax.management.NotCompliantMBeanException;
1415
import java.io.IOException;
1516
import java.lang.instrument.Instrumentation;
1617
import java.util.List;
@@ -28,7 +29,7 @@ public class Agent implements Callable<Void> {
2829
public Void call() throws Exception {
2930
System.setProperty("javax.management.builder.initial", JmxMBeanServerBuilder.class.getCanonicalName());
3031

31-
final MBeanServerInterceptorHarvester harvester = new MBeanServerInterceptorHarvester(harvesterOptions.exclusions, harvesterOptions.globalLabels);
32+
final MBeanServerInterceptorHarvester harvester = new MBeanServerInterceptorHarvester(harvesterOptions);
3233

3334
Server.start(httpServerOptions.listenAddresses, harvester, httpServerOptions.helpExposition);
3435

agent/src/main/java/com/zegelin/prometheus/cassandra/MBeanServerInterceptorHarvester.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package com.zegelin.prometheus.cassandra;
22

3+
import com.google.common.collect.ImmutableList;
34
import com.sun.jmx.mbeanserver.JmxMBeanServer;
45
import com.zegelin.jmx.DelegatingMBeanServerInterceptor;
5-
import org.slf4j.Logger;
6-
import org.slf4j.LoggerFactory;
6+
import com.zegelin.prometheus.cassandra.cli.HarvesterOptions;
77

88
import javax.management.*;
99
import java.lang.management.ManagementFactory;
10-
import java.util.Set;
10+
import java.lang.management.PlatformManagedObject;
11+
import java.util.List;
12+
import java.util.stream.Collectors;
1113

1214
class MBeanServerInterceptorHarvester extends Harvester {
13-
private static final Logger logger = LoggerFactory.getLogger(MBeanServerInterceptorHarvester.class);
14-
1515
class MBeanServerInterceptor extends DelegatingMBeanServerInterceptor {
1616
MBeanServerInterceptor(final MBeanServer delegate) {
1717
super(delegate);
@@ -41,10 +41,19 @@ public void unregisterMBean(final ObjectName mBeanName) throws InstanceNotFoundE
4141
}
4242
}
4343

44-
MBeanServerInterceptorHarvester(final Set<Exclusion> exclusions, final Set<GlobalLabel> globalLabels) {
45-
super(new FactoriesSupplier(new InternalMetadataFactory()), exclusions, globalLabels);
44+
MBeanServerInterceptorHarvester(final HarvesterOptions options) {
45+
super(new InternalMetadataFactory(), options);
4646

47+
registerPlatformMXBeans();
4748
registerMBeanServerInterceptor();
49+
50+
}
51+
52+
private void registerPlatformMXBeans() {
53+
ManagementFactory.getPlatformManagementInterfaces().stream()
54+
.flatMap(i -> ManagementFactory.getPlatformMXBeans(i).stream())
55+
.distinct()
56+
.forEach(mxBean -> registerMBean(mxBean, mxBean.getObjectName()));
4857
}
4958

5059
private void registerMBeanServerInterceptor() {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.zegelin.function;
2+
3+
@FunctionalInterface
4+
public interface FloatFloatFunction {
5+
float apply(float f);
6+
7+
static FloatFloatFunction identity() {
8+
return (f) -> f;
9+
}
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.zegelin.netty;
2+
3+
import info.adams.ryu.RyuFloat;
4+
import io.netty.buffer.ByteBuf;
5+
import io.netty.buffer.ByteBufUtil;
6+
7+
public final class Floats {
8+
public static boolean useFastFloat = true;
9+
10+
private Floats() {}
11+
12+
public static void writeFloatString(final ByteBuf buffer, final float f) {
13+
if (useFastFloat) {
14+
RyuFloat.floatToString(buffer, f);
15+
} else {
16+
ByteBufUtil.writeAscii(buffer, Float.toString(f));
17+
}
18+
}
19+
}

common/src/main/java/com/zegelin/netty/Resources.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.zegelin.netty;
22

33
import com.google.common.io.ByteStreams;
4-
import com.zegelin.prometheus.netty.HttpHandler;
54
import io.netty.buffer.ByteBuf;
65
import io.netty.buffer.Unpooled;
76

common/src/main/java/com/zegelin/prometheus/cassandra/CassandraMetricsUtilities.java

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
import com.codahale.metrics.Metric;
55
import com.codahale.metrics.Sampling;
66
import com.codahale.metrics.Snapshot;
7-
import com.google.common.collect.ImmutableMap;
8-
import com.google.common.collect.Maps;
97
import com.zegelin.jmx.NamedObject;
10-
import com.zegelin.prometheus.domain.Quantile;
8+
import com.zegelin.prometheus.domain.Interval;
119
import org.apache.cassandra.metrics.CassandraMetricsRegistry;
1210
import org.apache.cassandra.metrics.CassandraMetricsRegistry.JmxHistogramMBean;
1311
import org.apache.cassandra.metrics.CassandraMetricsRegistry.JmxTimerMBean;
@@ -16,6 +14,7 @@
1614
import java.util.HashMap;
1715
import java.util.Map;
1816
import java.util.function.Function;
17+
import java.util.stream.Stream;
1918

2019
@SuppressWarnings("Duplicates")
2120
public class CassandraMetricsUtilities {
@@ -64,15 +63,15 @@ public long getCount() {
6463
}
6564

6665
@Override
67-
public Map<Quantile, Float> getQuantiles() {
68-
return ImmutableMap.<Quantile, Float>builder()
69-
.put(Quantile.Q_50, (float) timer.get50thPercentile())
70-
.put(Quantile.Q_75, (float) timer.get75thPercentile())
71-
.put(Quantile.Q_95, (float) timer.get95thPercentile())
72-
.put(Quantile.Q_98, (float) timer.get98thPercentile())
73-
.put(Quantile.Q_99, (float) timer.get99thPercentile())
74-
.put(Quantile.Q_999, (float) timer.get999thPercentile())
75-
.build();
66+
public Stream<Interval> getIntervals() {
67+
return Stream.of(
68+
new Interval(Interval.Quantile.P_50, (float) timer.get50thPercentile()),
69+
new Interval(Interval.Quantile.P_75, (float) timer.get75thPercentile()),
70+
new Interval(Interval.Quantile.P_95, (float) timer.get95thPercentile()),
71+
new Interval(Interval.Quantile.P_98, (float) timer.get98thPercentile()),
72+
new Interval(Interval.Quantile.P_99, (float) timer.get99thPercentile()),
73+
new Interval(Interval.Quantile.P_99_9, (float) timer.get999thPercentile())
74+
);
7675
}
7776
};
7877
}
@@ -85,19 +84,20 @@ public long getCount() {
8584
}
8685

8786
@Override
88-
public Map<Quantile, Float> getQuantiles() {
89-
return ImmutableMap.<Quantile, Float>builder()
90-
.put(Quantile.Q_50, (float) histogram.get50thPercentile())
91-
.put(Quantile.Q_75, (float) histogram.get75thPercentile())
92-
.put(Quantile.Q_95, (float) histogram.get95thPercentile())
93-
.put(Quantile.Q_98, (float) histogram.get98thPercentile())
94-
.put(Quantile.Q_99, (float) histogram.get99thPercentile())
95-
.put(Quantile.Q_999, (float) histogram.get999thPercentile())
96-
.build();
87+
public Stream<Interval> getIntervals() {
88+
return Stream.of(
89+
new Interval(Interval.Quantile.P_50, (float) histogram.get50thPercentile()),
90+
new Interval(Interval.Quantile.P_75, (float) histogram.get75thPercentile()),
91+
new Interval(Interval.Quantile.P_95, (float) histogram.get95thPercentile()),
92+
new Interval(Interval.Quantile.P_98, (float) histogram.get98thPercentile()),
93+
new Interval(Interval.Quantile.P_99, (float) histogram.get99thPercentile()),
94+
new Interval(Interval.Quantile.P_99_9, (float) histogram.get999thPercentile())
95+
);
9796
}
9897
};
9998
}
10099

100+
101101
static <X extends Sampling & Counting> SamplingCounting adapt(final X metric) {
102102
return new SamplingCounting() {
103103
@Override
@@ -106,10 +106,10 @@ public long getCount() {
106106
}
107107

108108
@Override
109-
public Map<Quantile, Float> getQuantiles() {
109+
public Stream<Interval> getIntervals() {
110110
final Snapshot snapshot = metric.getSnapshot();
111111

112-
return Maps.toMap(Quantile.STANDARD_QUANTILES, q -> (float) snapshot.getValue(q.value));
112+
return Interval.asIntervals(Interval.Quantile.STANDARD_PERCENTILES, q -> (float) snapshot.getValue(q.value));
113113
}
114114
};
115115
}

common/src/main/java/com/zegelin/prometheus/cassandra/CollectorFunctions.java

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.zegelin.prometheus.cassandra;
22

3-
import com.google.common.collect.Maps;
3+
import com.zegelin.function.FloatFloatFunction;
44
import com.zegelin.prometheus.cassandra.collector.dynamic.FunctionalMetricFamilyCollector.CollectorFunction;
55
import com.zegelin.prometheus.cassandra.collector.dynamic.FunctionalMetricFamilyCollector.LabeledObjectGroup;
66
import com.zegelin.prometheus.domain.*;
@@ -9,14 +9,12 @@
99
import org.apache.cassandra.metrics.CassandraMetricsRegistry.JmxMeterMBean;
1010
import org.apache.cassandra.utils.EstimatedHistogram;
1111

12-
import java.util.Map;
13-
import java.util.function.Function;
1412
import java.util.stream.Stream;
1513

1614
public final class CollectorFunctions {
1715
private CollectorFunctions() {}
1816

19-
private static Stream<NumericMetric> counterMetricsStream(final LabeledObjectGroup<JmxCounterMBean> group, final Function<Float, Float> scaleFunction) {
17+
private static Stream<NumericMetric> counterMetricsStream(final LabeledObjectGroup<JmxCounterMBean> group, final FloatFloatFunction scaleFunction) {
2018
return group.labeledObjects().entrySet().stream()
2119
.map(e -> new Object() {
2220
final Labels labels = e.getKey();
@@ -28,7 +26,7 @@ private static Stream<NumericMetric> counterMetricsStream(final LabeledObjectGro
2826
/**
2927
* Collect a {@link JmxCounterMBean} as a Prometheus counter
3028
*/
31-
public static CollectorFunction<JmxCounterMBean> counterAsCounter(final Function<Float, Float> scaleFunction) {
29+
public static CollectorFunction<JmxCounterMBean> counterAsCounter(final FloatFloatFunction scaleFunction) {
3230
return group -> {
3331
final Stream<NumericMetric> metricStream = counterMetricsStream(group, scaleFunction);
3432

@@ -44,7 +42,7 @@ public static CollectorFunction<JmxCounterMBean> counterAsCounter() {
4442
/**
4543
* Collect a {@link JmxCounterMBean} as a Prometheus gauge
4644
*/
47-
public static CollectorFunction<JmxCounterMBean> counterAsGauge(final Function<Float, Float> scaleFunction) {
45+
public static CollectorFunction<JmxCounterMBean> counterAsGauge(final FloatFloatFunction scaleFunction) {
4846
return group -> {
4947
final Stream<NumericMetric> metricStream = counterMetricsStream(group, scaleFunction);
5048

@@ -53,15 +51,15 @@ public static CollectorFunction<JmxCounterMBean> counterAsGauge(final Function<F
5351
}
5452

5553
public static CollectorFunction<JmxCounterMBean> counterAsGauge() {
56-
return counterAsGauge(Function.identity());
54+
return counterAsGauge(FloatFloatFunction.identity());
5755
}
5856

5957

6058

6159
/**
6260
* Collect a {@link JmxMeterMBean} as a Prometheus counter
6361
*/
64-
public static CollectorFunction<JmxMeterMBean> meterAsCounter(final Function<Float, Float> scaleFunction) {
62+
public static CollectorFunction<JmxMeterMBean> meterAsCounter(final FloatFloatFunction scaleFunction) {
6563
return group -> {
6664
final Stream<NumericMetric> metricStream = group.labeledObjects().entrySet().stream()
6765
.map(e -> new Object() {
@@ -76,11 +74,11 @@ public static CollectorFunction<JmxMeterMBean> meterAsCounter(final Function<Flo
7674
}
7775

7876
public static CollectorFunction<JmxMeterMBean> meterAsCounter() {
79-
return meterAsCounter(Function.identity());
77+
return meterAsCounter(FloatFloatFunction.identity());
8078
}
8179

8280

83-
private static Stream<NumericMetric> numericGaugeMetricsStream(final LabeledObjectGroup<JmxGaugeMBean> group, final Function<Float, Float> scaleFunction) {
81+
private static Stream<NumericMetric> numericGaugeMetricsStream(final LabeledObjectGroup<JmxGaugeMBean> group, final FloatFloatFunction scaleFunction) {
8482
return group.labeledObjects().entrySet().stream()
8583
.map(e -> new Object() {
8684
final Labels labels = e.getKey();
@@ -92,7 +90,7 @@ private static Stream<NumericMetric> numericGaugeMetricsStream(final LabeledObje
9290
/**
9391
* Collect a {@link JmxGaugeMBean} with a {@link Number} value as a Prometheus gauge
9492
*/
95-
public static CollectorFunction<JmxGaugeMBean> numericGaugeAsGauge(final Function<Float, Float> scaleFunction) {
93+
public static CollectorFunction<JmxGaugeMBean> numericGaugeAsGauge(final FloatFloatFunction scaleFunction) {
9694
return group -> {
9795
final Stream<NumericMetric> metricStream = numericGaugeMetricsStream(group, scaleFunction);
9896

@@ -101,14 +99,14 @@ public static CollectorFunction<JmxGaugeMBean> numericGaugeAsGauge(final Functio
10199
}
102100

103101
public static CollectorFunction<JmxGaugeMBean> numericGaugeAsGauge() {
104-
return numericGaugeAsGauge(Function.identity());
102+
return numericGaugeAsGauge(FloatFloatFunction.identity());
105103
}
106104

107105

108106
/**
109107
* Collect a {@link JmxGaugeMBean} with a {@see Number} value as a Prometheus counter
110108
*/
111-
public static CollectorFunction<JmxGaugeMBean> numericGaugeAsCounter(final Function<Float, Float> scaleFunction) {
109+
public static CollectorFunction<JmxGaugeMBean> numericGaugeAsCounter(final FloatFloatFunction scaleFunction) {
112110
return group -> {
113111
final Stream<NumericMetric> metricStream = numericGaugeMetricsStream(group, scaleFunction);
114112

@@ -117,15 +115,15 @@ public static CollectorFunction<JmxGaugeMBean> numericGaugeAsCounter(final Funct
117115
}
118116

119117
public static CollectorFunction<JmxGaugeMBean> numericGaugeAsCounter() {
120-
return numericGaugeAsCounter(Function.identity());
118+
return numericGaugeAsCounter(FloatFloatFunction.identity());
121119
}
122120

123121

124122

125123
/**
126124
* Collect a {@link JmxGaugeMBean} with a Cassandra {@link EstimatedHistogram} value as a Prometheus summary
127125
*/
128-
public static CollectorFunction<JmxGaugeMBean> histogramGaugeAsSummary(final Function<Float, Float> quantileScaleFunction) {
126+
public static CollectorFunction<JmxGaugeMBean> histogramGaugeAsSummary(final FloatFloatFunction bucketScaleFunction) {
129127
return group -> {
130128
final Stream<SummaryMetricFamily.Summary> summaryStream = group.labeledObjects().entrySet().stream()
131129
.map(e -> new Object() {
@@ -136,12 +134,12 @@ public static CollectorFunction<JmxGaugeMBean> histogramGaugeAsSummary(final Fun
136134
final long[] bucketData = (long[]) e.gauge.getValue();
137135

138136
if (bucketData.length == 0) {
139-
return new SummaryMetricFamily.Summary(e.labels, Float.NaN, Float.NaN, Maps.toMap(Quantile.STANDARD_QUANTILES, q -> Float.NaN));
137+
return new SummaryMetricFamily.Summary(e.labels, Float.NaN, Float.NaN, Interval.asIntervals(Interval.Quantile.STANDARD_PERCENTILES, q -> Float.NaN));
140138
}
141139

142140
final EstimatedHistogram histogram = new EstimatedHistogram(bucketData);
143141

144-
final Map<Quantile, Float> quantiles = Maps.toMap(Quantile.STANDARD_QUANTILES, q -> quantileScaleFunction.apply((float) histogram.percentile(q.value)));
142+
final Stream<Interval> quantiles = Interval.asIntervals(Interval.Quantile.STANDARD_PERCENTILES, q -> bucketScaleFunction.apply((float) histogram.percentile(q.value)));
145143

146144
return new SummaryMetricFamily.Summary(e.labels, Float.NaN, histogram.count(), quantiles);
147145
});
@@ -157,15 +155,16 @@ public static CollectorFunction<JmxGaugeMBean> histogramGaugeAsSummary() {
157155
/**
158156
* Collect a {@link SamplingCounting} as a Prometheus summary
159157
*/
160-
protected static CollectorFunction<SamplingCounting> samplingAndCountingAsSummary(final Function<Float, Float> quantileScaleFunction) {
158+
protected static CollectorFunction<SamplingCounting> samplingAndCountingAsSummary(final FloatFloatFunction quantileScaleFunction) {
161159
return group -> {
162160
final Stream<SummaryMetricFamily.Summary> summaryStream = group.labeledObjects().entrySet().stream()
163161
.map(e -> new Object() {
164162
final Labels labels = e.getKey();
165163
final SamplingCounting samplingCounting = e.getValue();
166164
})
167165
.map(e -> {
168-
final Map<Quantile, Float> quantiles = Maps.transformValues(e.samplingCounting.getQuantiles(), v -> quantileScaleFunction.apply(v.floatValue()));
166+
final Stream<Interval> quantiles = e.samplingCounting.getIntervals()
167+
.map(i -> i.transform(quantileScaleFunction));
169168

170169
return new SummaryMetricFamily.Summary(e.labels, Float.NaN, e.samplingCounting.getCount(), quantiles);
171170
});
@@ -175,6 +174,6 @@ protected static CollectorFunction<SamplingCounting> samplingAndCountingAsSummar
175174
}
176175

177176
public static CollectorFunction<SamplingCounting> samplingAndCountingAsSummary() {
178-
return samplingAndCountingAsSummary(Function.identity());
177+
return samplingAndCountingAsSummary(FloatFloatFunction.identity());
179178
}
180179
}

0 commit comments

Comments
 (0)