Skip to content
This repository was archived by the owner on Feb 29, 2020. It is now read-only.

Commit 3fc8c10

Browse files
author
Michal Witkowski
committed
Add basic JVM metrics: MemoryPoolsExports GarbageCollectorExports.
1 parent 9414541 commit 3fc8c10

File tree

4 files changed

+416
-0
lines changed

4 files changed

+416
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package io.prometheus.client.hotspot;
2+
3+
import io.prometheus.client.Collector;
4+
5+
import java.lang.management.GarbageCollectorMXBean;
6+
import java.lang.management.ManagementFactory;
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.regex.Pattern;
12+
13+
/**
14+
* Exports metrics about JVM garbage collectors.
15+
* <p>
16+
* Example usage:
17+
* <pre>
18+
* {@code
19+
* new GarbageCollectorExports().register();
20+
* }
21+
* </pre>
22+
* Example metrics being exported:
23+
* <pre>
24+
* jvm_gc_collections{gc="PS1} 200
25+
* jvm_gc_collections_time{gc="PS1} 6.7
26+
* </pre>
27+
*/
28+
public class GarbageCollectorExports extends Collector {
29+
private static final Pattern WHITESPACE = Pattern.compile("[\\s]+");
30+
static final String COLLECTIONS_COUNT_METRIC = "jvm_gc_collections";
31+
static final String COLLECTIONS_TIME_METRIC = "jvm_gc_collections_time";
32+
static final List<String> LABEL_NAMES = Arrays.asList("gc");
33+
private static final List<String> DEFAULT_LABEL = Arrays.asList("unknown");
34+
35+
private final HashMap<GarbageCollectorMXBean, List<String>> labelValues =
36+
new HashMap<GarbageCollectorMXBean, List<String>>();
37+
private final List<GarbageCollectorMXBean> garbageCollectors;
38+
39+
public GarbageCollectorExports() {
40+
this(ManagementFactory.getGarbageCollectorMXBeans());
41+
}
42+
43+
GarbageCollectorExports(List<GarbageCollectorMXBean> garbageCollectors) {
44+
this.garbageCollectors = garbageCollectors;
45+
for (final GarbageCollectorMXBean gc : garbageCollectors) {
46+
if (!labelValues.containsKey(gc)) {
47+
String gcName = WHITESPACE.matcher(gc.getName()).replaceAll("-");
48+
labelValues.put(gc, Arrays.asList(gcName));
49+
}
50+
}
51+
}
52+
53+
MetricFamilySamples collectorCountMetric() {
54+
ArrayList<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>();
55+
for (final GarbageCollectorMXBean gc : garbageCollectors) {
56+
samples.add(
57+
new MetricFamilySamples.Sample(
58+
COLLECTIONS_COUNT_METRIC,
59+
LABEL_NAMES,
60+
labelValues.getOrDefault(gc, DEFAULT_LABEL),
61+
gc.getCollectionCount()));
62+
}
63+
return new MetricFamilySamples(
64+
COLLECTIONS_COUNT_METRIC,
65+
Type.COUNTER,
66+
"Number of collections of a given JVM garbage collector.",
67+
samples);
68+
}
69+
70+
MetricFamilySamples collectorTimeMetric() {
71+
ArrayList<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>();
72+
for (final GarbageCollectorMXBean gc : garbageCollectors) {
73+
samples.add(
74+
new MetricFamilySamples.Sample(
75+
COLLECTIONS_TIME_METRIC,
76+
LABEL_NAMES,
77+
labelValues.getOrDefault(gc, DEFAULT_LABEL),
78+
gc.getCollectionTime() / MILLISECONDS_PER_SECOND));
79+
}
80+
return new MetricFamilySamples(
81+
COLLECTIONS_TIME_METRIC,
82+
Type.COUNTER,
83+
"Accumulated time (s) spent in a given JVM garbage collector.",
84+
samples);
85+
}
86+
87+
public List<MetricFamilySamples> collect() {
88+
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
89+
mfs.add(collectorCountMetric());
90+
mfs.add(collectorTimeMetric());
91+
92+
return mfs;
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package io.prometheus.client.hotspot;
2+
3+
import io.prometheus.client.Collector;
4+
5+
import java.lang.management.ManagementFactory;
6+
import java.lang.management.MemoryMXBean;
7+
import java.lang.management.MemoryPoolMXBean;
8+
import java.lang.management.MemoryUsage;
9+
import java.util.ArrayList;
10+
import java.util.Arrays;
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.regex.Pattern;
14+
15+
/**
16+
* Exports metrics about JVM memory areas.
17+
* <p>
18+
* Example usage:
19+
* <pre>
20+
* {@code
21+
* new MemoryPoolsExports().register();
22+
* }
23+
* </pre>
24+
* Example metrics being exported:
25+
* <pre>
26+
* jvm_memory_used{area="heap} 2000000
27+
* jvm_memory_limit{area="nonheap"} 200000
28+
* jvm_memory_pool_used{pool="PS-Eden-Space"} 2000
29+
* </pre>
30+
*/
31+
public class MemoryPoolsExports extends Collector {
32+
private static final Pattern WHITESPACE = Pattern.compile("[\\s]+");
33+
static final String MEMORY_USED_METRIC = "jvm_memory_used";
34+
static final String MEMORY_LIMIT_METRIC = "jvm_memory_limit";
35+
static final String POOLS_USED_METRIC = "jvm_memory_pool_used";
36+
static final String POOLS_LIMIT_METRIC = "jvm_memory_pool_limit";
37+
38+
private static final List<String> MEMORY_LABEL_NAMES = Arrays.asList("area");
39+
private static final List<String> MEMORY_HEAP_LABEL = Arrays.asList("heap");
40+
private static final List<String> MEMORY_NONHEAP_LABEL = Arrays.asList("nonheap");
41+
42+
private static final List<String> POOLS_LABEL_NAMES = Arrays.asList("pool");
43+
44+
private final HashMap<MemoryPoolMXBean, List<String>> poolLabelValues = new HashMap<MemoryPoolMXBean, List<String>>();
45+
private final MemoryMXBean memoryBean;
46+
private final List<MemoryPoolMXBean> poolBeans;
47+
48+
public MemoryPoolsExports() {
49+
this(
50+
ManagementFactory.getMemoryMXBean(),
51+
ManagementFactory.getMemoryPoolMXBeans());
52+
}
53+
54+
public MemoryPoolsExports(MemoryMXBean memoryBean,
55+
List<MemoryPoolMXBean> poolBeans) {
56+
this.memoryBean = memoryBean;
57+
this.poolBeans = poolBeans;
58+
for (final MemoryPoolMXBean pool : poolBeans) {
59+
if (!poolLabelValues.containsKey(pool)) {
60+
String gcName = WHITESPACE.matcher(pool.getName()).replaceAll("-");
61+
poolLabelValues.put(pool, Arrays.asList(gcName));
62+
}
63+
}
64+
}
65+
66+
void addMemoryAreaMetrics(List<MetricFamilySamples> sampleFamilies) {
67+
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
68+
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
69+
ArrayList<MetricFamilySamples.Sample> usedSamples = new ArrayList<MetricFamilySamples.Sample>();
70+
usedSamples.add(
71+
new MetricFamilySamples.Sample(
72+
MEMORY_USED_METRIC,
73+
MEMORY_LABEL_NAMES,
74+
MEMORY_HEAP_LABEL,
75+
heapUsage.getUsed()));
76+
usedSamples.add(
77+
new MetricFamilySamples.Sample(
78+
MEMORY_USED_METRIC,
79+
MEMORY_LABEL_NAMES,
80+
MEMORY_NONHEAP_LABEL,
81+
nonHeapUsage.getUsed()));
82+
sampleFamilies.add(
83+
new MetricFamilySamples(
84+
MEMORY_USED_METRIC,
85+
Type.GAUGE,
86+
"Used bytes of a given JVM memory area (heap, nonheap).",
87+
usedSamples));
88+
ArrayList<MetricFamilySamples.Sample> limitSamples = new ArrayList<MetricFamilySamples.Sample>();
89+
limitSamples.add(
90+
new MetricFamilySamples.Sample(
91+
MEMORY_LIMIT_METRIC,
92+
MEMORY_LABEL_NAMES,
93+
MEMORY_HEAP_LABEL,
94+
heapUsage.getMax() == -1 ? heapUsage.getMax() : heapUsage.getCommitted()));
95+
limitSamples.add(
96+
new MetricFamilySamples.Sample(
97+
MEMORY_LIMIT_METRIC,
98+
MEMORY_LABEL_NAMES,
99+
MEMORY_NONHEAP_LABEL,
100+
nonHeapUsage.getMax() == -1 ? nonHeapUsage.getMax() : nonHeapUsage.getCommitted()));
101+
sampleFamilies.add(
102+
new MetricFamilySamples(
103+
MEMORY_LIMIT_METRIC,
104+
Type.GAUGE,
105+
"Limit (bytes) of a given JVM memory area (heap, nonheap).",
106+
limitSamples));
107+
}
108+
109+
void addMemoryPoolMetrics(List<MetricFamilySamples> sampleFamilies) {
110+
ArrayList<MetricFamilySamples.Sample> usedSamples = new ArrayList<MetricFamilySamples.Sample>();
111+
ArrayList<MetricFamilySamples.Sample> limitSamples = new ArrayList<MetricFamilySamples.Sample>();
112+
for (final MemoryPoolMXBean pool : poolBeans) {
113+
MemoryUsage poolUsage = pool.getUsage();
114+
usedSamples.add(
115+
new MetricFamilySamples.Sample(
116+
POOLS_USED_METRIC,
117+
POOLS_LABEL_NAMES,
118+
poolLabelValues.get(pool),
119+
poolUsage.getUsed()));
120+
limitSamples.add(
121+
new MetricFamilySamples.Sample(
122+
POOLS_LIMIT_METRIC,
123+
POOLS_LABEL_NAMES,
124+
poolLabelValues.get(pool),
125+
poolUsage.getMax() != -1 ? poolUsage.getMax() : poolUsage.getCommitted()));
126+
}
127+
sampleFamilies.add(
128+
new MetricFamilySamples(
129+
POOLS_USED_METRIC,
130+
Type.GAUGE,
131+
"Used bytes of a given JVM memory pool.",
132+
usedSamples));
133+
134+
sampleFamilies.add(
135+
new MetricFamilySamples(
136+
POOLS_LIMIT_METRIC,
137+
Type.GAUGE,
138+
"Limit (bytes) of a given JVM memory pool.",
139+
limitSamples));
140+
}
141+
142+
143+
public List<MetricFamilySamples> collect() {
144+
List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
145+
addMemoryAreaMetrics(mfs);
146+
addMemoryPoolMetrics(mfs);
147+
return mfs;
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.prometheus.client.hotspot;
2+
3+
import io.prometheus.client.CollectorRegistry;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
import org.mockito.Mockito;
7+
8+
import java.lang.management.GarbageCollectorMXBean;
9+
import java.util.Arrays;
10+
import java.util.List;
11+
import java.util.concurrent.TimeUnit;
12+
13+
import static org.junit.Assert.assertEquals;
14+
15+
16+
public class GarbageCollectorExportsTest {
17+
18+
private GarbageCollectorMXBean mockGcBean1 = Mockito.mock(GarbageCollectorMXBean.class);
19+
private GarbageCollectorMXBean mockGcBean2 = Mockito.mock(GarbageCollectorMXBean.class);
20+
private List<GarbageCollectorMXBean> mockList = Arrays.asList(mockGcBean1, mockGcBean2);
21+
private CollectorRegistry registry = new CollectorRegistry();
22+
private GarbageCollectorExports collectorUnderTest;
23+
24+
@Before
25+
public void setUp() {
26+
Mockito.when(mockGcBean1.getName()).thenReturn("MyGC1");
27+
Mockito.when(mockGcBean1.getCollectionCount()).thenReturn(100L);
28+
Mockito.when(mockGcBean1.getCollectionTime()).thenReturn(TimeUnit.SECONDS.toMillis(10));
29+
Mockito.when(mockGcBean2.getName()).thenReturn("MyGC2");
30+
Mockito.when(mockGcBean2.getCollectionCount()).thenReturn(200L);
31+
Mockito.when(mockGcBean2.getCollectionTime()).thenReturn(TimeUnit.SECONDS.toMillis(20));
32+
collectorUnderTest = new GarbageCollectorExports(mockList).register(registry);
33+
}
34+
35+
@Test
36+
public void testGarbageCollectorExports() {
37+
assertEquals(
38+
100L,
39+
registry.getSampleValue(
40+
GarbageCollectorExports.COLLECTIONS_COUNT_METRIC,
41+
new String[]{"gc"},
42+
new String[]{"MyGC1"}),
43+
.0000001);
44+
assertEquals(
45+
10d,
46+
registry.getSampleValue(
47+
GarbageCollectorExports.COLLECTIONS_TIME_METRIC,
48+
new String[]{"gc"},
49+
new String[]{"MyGC1"}),
50+
.0000001);
51+
assertEquals(
52+
200L,
53+
registry.getSampleValue(
54+
GarbageCollectorExports.COLLECTIONS_COUNT_METRIC,
55+
new String[]{"gc"},
56+
new String[]{"MyGC2"}),
57+
.0000001);
58+
assertEquals(
59+
20d,
60+
registry.getSampleValue(
61+
GarbageCollectorExports.COLLECTIONS_TIME_METRIC,
62+
new String[]{"gc"},
63+
new String[]{"MyGC2"}),
64+
.0000001);
65+
}
66+
}

0 commit comments

Comments
 (0)