Skip to content

Commit ec56b2c

Browse files
authored
add support for caffeine instrumentation with 1.X.X (#983)
* add support for caffeine instrumentation with 1.X.X --------- Signed-off-by: Petar Heyken <p.heyken@rebuy.com>
1 parent c615b0a commit ec56b2c

File tree

7 files changed

+412
-266
lines changed

7 files changed

+412
-266
lines changed

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<module>prometheus-metrics-exporter-httpserver</module>
6565
<module>prometheus-metrics-exporter-opentelemetry</module>
6666
<module>prometheus-metrics-exporter-pushgateway</module>
67+
<module>prometheus-metrics-instrumentation-caffeine</module>
6768
<module>prometheus-metrics-instrumentation-jvm</module>
6869
<module>prometheus-metrics-instrumentation-dropwizard5</module>
6970
<module>prometheus-metrics-instrumentation-guava</module>

simpleclient-archive/simpleclient_caffeine/pom.xml prometheus-metrics-instrumentation-caffeine/pom.xml

+26-8
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,21 @@
55
<parent>
66
<groupId>io.prometheus</groupId>
77
<artifactId>client_java</artifactId>
8-
<version>1.0.0-beta-2-SNAPSHOT</version>
8+
<version>1.4.0-SNAPSHOT</version>
99
</parent>
1010

11-
<artifactId>simpleclient_caffeine</artifactId>
11+
<artifactId>prometheus-metrics-instrumentation-caffeine</artifactId>
1212
<packaging>bundle</packaging>
1313

14-
<name>Prometheus Java Simpleclient Caffeine</name>
14+
<name>Prometheus Metrics Instrumentation - Caffeine</name>
1515
<description>
16-
Metrics collector for caffeine based caches
16+
Instrumentation library for caffeine based caches
1717
</description>
1818

19+
<properties>
20+
<automatic.module.name>io.prometheus.metrics.instrumentation.caffeine</automatic.module.name>
21+
</properties>
22+
1923
<licenses>
2024
<license>
2125
<name>The Apache Software License, Version 2.0</name>
@@ -30,18 +34,25 @@
3034
<name>Clint Checketts</name>
3135
<email>checketts@gmail.com</email>
3236
</developer>
37+
38+
<developer>
39+
<id>pheyken</id>
40+
<name>Petar Heyken</name>
41+
<email>mail@petar-heyken.de</email>
42+
</developer>
3343
</developers>
3444

3545
<dependencies>
3646
<dependency>
3747
<groupId>io.prometheus</groupId>
38-
<artifactId>simpleclient</artifactId>
48+
<artifactId>prometheus-metrics-core</artifactId>
3949
<version>${project.version}</version>
4050
</dependency>
4151
<dependency>
4252
<groupId>com.github.ben-manes.caffeine</groupId>
4353
<artifactId>caffeine</artifactId>
44-
<version>3.1.1</version>
54+
<version>3.1.8</version>
55+
<scope>provided</scope>
4556
</dependency>
4657

4758

@@ -55,13 +66,20 @@
5566
<dependency>
5667
<groupId>org.mockito</groupId>
5768
<artifactId>mockito-core</artifactId>
58-
<version>4.6.1</version>
69+
<version>5.12.0</version>
5970
<scope>test</scope>
6071
</dependency>
6172
<dependency>
6273
<groupId>org.assertj</groupId>
6374
<artifactId>assertj-core</artifactId>
64-
<version>3.23.1</version>
75+
<version>3.26.3</version>
76+
<scope>test</scope>
77+
</dependency>
78+
79+
<dependency>
80+
<groupId>io.prometheus</groupId>
81+
<artifactId>prometheus-metrics-exposition-formats</artifactId>
82+
<version>${project.version}</version>
6583
<scope>test</scope>
6684
</dependency>
6785

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package io.prometheus.metrics.instrumentation.caffeine;
2+
3+
import com.github.benmanes.caffeine.cache.AsyncCache;
4+
import com.github.benmanes.caffeine.cache.Cache;
5+
import com.github.benmanes.caffeine.cache.LoadingCache;
6+
import com.github.benmanes.caffeine.cache.stats.CacheStats;
7+
import io.prometheus.metrics.model.registry.MultiCollector;
8+
import io.prometheus.metrics.model.snapshots.CounterSnapshot;
9+
import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
10+
import io.prometheus.metrics.model.snapshots.Labels;
11+
import io.prometheus.metrics.model.snapshots.MetricSnapshots;
12+
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
13+
14+
import java.util.Arrays;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.concurrent.ConcurrentHashMap;
18+
import java.util.concurrent.ConcurrentMap;
19+
20+
21+
/**
22+
* Collect metrics from Caffeine's com.github.benmanes.caffeine.cache.Cache.
23+
* <p>
24+
* <pre>{@code
25+
*
26+
* // Note that `recordStats()` is required to gather non-zero statistics
27+
* Cache<String, String> cache = Caffeine.newBuilder().recordStats().build();
28+
* CacheMetricsCollector cacheMetrics = new CacheMetricsCollector().register();
29+
* cacheMetrics.addCache("mycache", cache);
30+
*
31+
* }</pre>
32+
*
33+
* Exposed metrics are labeled with the provided cache name.
34+
*
35+
* With the example above, sample metric names would be:
36+
* <pre>
37+
* caffeine_cache_hit_total{cache="mycache"} 10.0
38+
* caffeine_cache_miss_total{cache="mycache"} 3.0
39+
* caffeine_cache_requests_total{cache="mycache"} 13.0
40+
* caffeine_cache_eviction_total{cache="mycache"} 1.0
41+
* caffeine_cache_estimated_size{cache="mycache"} 5.0
42+
* </pre>
43+
*
44+
* Additionally, if the cache includes a loader, the following metrics would be provided:
45+
* <pre>
46+
* caffeine_cache_load_failure_total{cache="mycache"} 2.0
47+
* caffeine_cache_loads_total{cache="mycache"} 7.0
48+
* caffeine_cache_load_duration_seconds_count{cache="mycache"} 7.0
49+
* caffeine_cache_load_duration_seconds_sum{cache="mycache"} 0.0034
50+
* </pre>
51+
*
52+
*/
53+
public class CacheMetricsCollector implements MultiCollector {
54+
private static final double NANOSECONDS_PER_SECOND = 1_000_000_000.0;
55+
56+
protected final ConcurrentMap<String, Cache> children = new ConcurrentHashMap<String, Cache>();
57+
58+
/**
59+
* Add or replace the cache with the given name.
60+
* <p>
61+
* Any references any previous cache with this name is invalidated.
62+
*
63+
* @param cacheName The name of the cache, will be the metrics label value
64+
* @param cache The cache being monitored
65+
*/
66+
public void addCache(String cacheName, Cache cache) {
67+
children.put(cacheName, cache);
68+
}
69+
70+
/**
71+
* Add or replace the cache with the given name.
72+
* <p>
73+
* Any references any previous cache with this name is invalidated.
74+
*
75+
* @param cacheName The name of the cache, will be the metrics label value
76+
* @param cache The cache being monitored
77+
*/
78+
public void addCache(String cacheName, AsyncCache cache) {
79+
children.put(cacheName, cache.synchronous());
80+
}
81+
82+
/**
83+
* Remove the cache with the given name.
84+
* <p>
85+
* Any references to the cache are invalidated.
86+
*
87+
* @param cacheName cache to be removed
88+
*/
89+
public Cache removeCache(String cacheName) {
90+
return children.remove(cacheName);
91+
}
92+
93+
/**
94+
* Remove all caches.
95+
* <p>
96+
* Any references to all caches are invalidated.
97+
*/
98+
public void clear(){
99+
children.clear();
100+
}
101+
102+
@Override
103+
public MetricSnapshots collect() {
104+
final MetricSnapshots.Builder metricSnapshotsBuilder = MetricSnapshots.builder();
105+
final List<String> labelNames = Arrays.asList("cache");
106+
107+
final CounterSnapshot.Builder cacheHitTotal = CounterSnapshot.builder()
108+
.name("caffeine_cache_hit")
109+
.help("Cache hit totals");
110+
111+
final CounterSnapshot.Builder cacheMissTotal = CounterSnapshot.builder()
112+
.name("caffeine_cache_miss")
113+
.help("Cache miss totals");
114+
115+
final CounterSnapshot.Builder cacheRequestsTotal = CounterSnapshot.builder()
116+
.name("caffeine_cache_requests")
117+
.help("Cache request totals, hits + misses");
118+
119+
final CounterSnapshot.Builder cacheEvictionTotal = CounterSnapshot.builder()
120+
.name("caffeine_cache_eviction")
121+
.help("Cache eviction totals, doesn't include manually removed entries");
122+
123+
final GaugeSnapshot.Builder cacheEvictionWeight = GaugeSnapshot.builder()
124+
.name("caffeine_cache_eviction_weight")
125+
.help("Cache eviction weight");
126+
127+
final CounterSnapshot.Builder cacheLoadFailure = CounterSnapshot.builder()
128+
.name("caffeine_cache_load_failure")
129+
.help("Cache load failures");
130+
131+
final CounterSnapshot.Builder cacheLoadTotal = CounterSnapshot.builder()
132+
.name("caffeine_cache_loads")
133+
.help("Cache loads: both success and failures");
134+
135+
final GaugeSnapshot.Builder cacheSize = GaugeSnapshot.builder()
136+
.name("caffeine_cache_estimated_size")
137+
.help("Estimated cache size");
138+
139+
final SummarySnapshot.Builder cacheLoadSummary = SummarySnapshot.builder()
140+
.name("caffeine_cache_load_duration_seconds")
141+
.help("Cache load duration: both success and failures");
142+
143+
for (final Map.Entry<String, Cache> c: children.entrySet()) {
144+
final List<String> cacheName = Arrays.asList(c.getKey());
145+
final Labels labels = Labels.of(labelNames, cacheName);
146+
147+
final CacheStats stats = c.getValue().stats();
148+
149+
try {
150+
cacheEvictionWeight.dataPoint(
151+
GaugeSnapshot.GaugeDataPointSnapshot.builder()
152+
.labels(labels)
153+
.value(stats.evictionWeight())
154+
.build()
155+
);
156+
} catch (Exception e) {
157+
// EvictionWeight metric is unavailable, newer version of Caffeine is needed.
158+
}
159+
160+
cacheHitTotal.dataPoint(
161+
CounterSnapshot.CounterDataPointSnapshot.builder()
162+
.labels(labels)
163+
.value(stats.hitCount())
164+
.build()
165+
);
166+
167+
cacheMissTotal.dataPoint(
168+
CounterSnapshot.CounterDataPointSnapshot.builder()
169+
.labels(labels)
170+
.value(stats.missCount())
171+
.build()
172+
);
173+
174+
cacheRequestsTotal.dataPoint(
175+
CounterSnapshot.CounterDataPointSnapshot.builder()
176+
.labels(labels)
177+
.value(stats.requestCount())
178+
.build()
179+
);
180+
181+
cacheEvictionTotal.dataPoint(
182+
CounterSnapshot.CounterDataPointSnapshot.builder()
183+
.labels(labels)
184+
.value(stats.evictionCount())
185+
.build()
186+
);
187+
188+
cacheSize.dataPoint(
189+
GaugeSnapshot.GaugeDataPointSnapshot.builder()
190+
.labels(labels)
191+
.value(c.getValue().estimatedSize())
192+
.build()
193+
);
194+
195+
if (c.getValue() instanceof LoadingCache) {
196+
cacheLoadFailure.dataPoint(
197+
CounterSnapshot.CounterDataPointSnapshot.builder()
198+
.labels(labels)
199+
.value(stats.loadFailureCount())
200+
.build()
201+
);
202+
203+
cacheLoadTotal.dataPoint(
204+
CounterSnapshot.CounterDataPointSnapshot.builder()
205+
.labels(labels)
206+
.value(stats.loadCount())
207+
.build()
208+
);
209+
210+
cacheLoadSummary.dataPoint(
211+
SummarySnapshot.SummaryDataPointSnapshot.builder()
212+
.labels(labels)
213+
.count(stats.loadCount())
214+
.sum(stats.totalLoadTime() / NANOSECONDS_PER_SECOND)
215+
.build()
216+
);
217+
}
218+
}
219+
220+
return metricSnapshotsBuilder
221+
.metricSnapshot(cacheHitTotal.build())
222+
.metricSnapshot(cacheMissTotal.build())
223+
.metricSnapshot(cacheRequestsTotal.build())
224+
.metricSnapshot(cacheEvictionTotal.build())
225+
.metricSnapshot(cacheEvictionWeight.build())
226+
.metricSnapshot(cacheLoadFailure.build())
227+
.metricSnapshot(cacheLoadTotal.build())
228+
.metricSnapshot(cacheSize.build())
229+
.metricSnapshot(cacheLoadSummary.build())
230+
.build();
231+
}
232+
}

0 commit comments

Comments
 (0)