diff --git a/prometheus-metrics-instrumentation-caffeine/src/main/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollector.java b/prometheus-metrics-instrumentation-caffeine/src/main/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollector.java index 59bd28a9c..19a906146 100644 --- a/prometheus-metrics-instrumentation-caffeine/src/main/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollector.java +++ b/prometheus-metrics-instrumentation-caffeine/src/main/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollector.java @@ -3,6 +3,7 @@ import com.github.benmanes.caffeine.cache.AsyncCache; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.LoadingCache; +import com.github.benmanes.caffeine.cache.Policy; import com.github.benmanes.caffeine.cache.stats.CacheStats; import io.prometheus.metrics.model.registry.MultiCollector; import io.prometheus.metrics.model.snapshots.CounterSnapshot; @@ -14,6 +15,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -63,6 +65,7 @@ public class CacheMetricsCollector implements MultiCollector { private static final String METRIC_NAME_CACHE_LOAD_FAILURE = "caffeine_cache_load_failure"; private static final String METRIC_NAME_CACHE_LOADS = "caffeine_cache_loads"; private static final String METRIC_NAME_CACHE_ESTIMATED_SIZE = "caffeine_cache_estimated_size"; + private static final String METRIC_NAME_CACHE_WEIGHTED_SIZE = "caffeine_cache_weighted_size"; private static final String METRIC_NAME_CACHE_LOAD_DURATION_SECONDS = "caffeine_cache_load_duration_seconds"; @@ -77,6 +80,7 @@ public class CacheMetricsCollector implements MultiCollector { METRIC_NAME_CACHE_LOAD_FAILURE, METRIC_NAME_CACHE_LOADS, METRIC_NAME_CACHE_ESTIMATED_SIZE, + METRIC_NAME_CACHE_WEIGHTED_SIZE, METRIC_NAME_CACHE_LOAD_DURATION_SECONDS)); protected final ConcurrentMap> children = new ConcurrentHashMap<>(); @@ -162,6 +166,11 @@ public MetricSnapshots collect() { final GaugeSnapshot.Builder cacheSize = GaugeSnapshot.builder().name(METRIC_NAME_CACHE_ESTIMATED_SIZE).help("Estimated cache size"); + final GaugeSnapshot.Builder cacheWeightedSize = + GaugeSnapshot.builder() + .name(METRIC_NAME_CACHE_WEIGHTED_SIZE) + .help("Approximate accumulated weight of cache entries"); + final SummarySnapshot.Builder cacheLoadSummary = SummarySnapshot.builder() .name(METRIC_NAME_CACHE_LOAD_DURATION_SECONDS) @@ -183,6 +192,15 @@ public MetricSnapshots collect() { // EvictionWeight metric is unavailable, newer version of Caffeine is needed. } + final Optional> eviction = c.getValue().policy().eviction(); + if (eviction.isPresent() && eviction.get().weightedSize().isPresent()) { + cacheWeightedSize.dataPoint( + GaugeSnapshot.GaugeDataPointSnapshot.builder() + .labels(labels) + .value(eviction.get().weightedSize().getAsLong()) + .build()); + } + cacheHitTotal.dataPoint( CounterSnapshot.CounterDataPointSnapshot.builder() .labels(labels) @@ -244,6 +262,7 @@ public MetricSnapshots collect() { .metricSnapshot(cacheLoadFailure.build()) .metricSnapshot(cacheLoadTotal.build()) .metricSnapshot(cacheSize.build()) + .metricSnapshot(cacheWeightedSize.build()) .metricSnapshot(cacheLoadSummary.build()) .build(); } diff --git a/prometheus-metrics-instrumentation-caffeine/src/test/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollectorTest.java b/prometheus-metrics-instrumentation-caffeine/src/test/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollectorTest.java index f284d128f..5d53813be 100644 --- a/prometheus-metrics-instrumentation-caffeine/src/test/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollectorTest.java +++ b/prometheus-metrics-instrumentation-caffeine/src/test/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollectorTest.java @@ -78,6 +78,66 @@ public void cacheExposesMetricsForHitMissAndEviction() { assertThat(convertToOpenMetricsFormat(registry)).isEqualTo(expected); } + @Test + public void weightedCacheExposesMetricsForHitMissAndEvictionWeightedSize() { + // Run cleanup in same thread, to remove async behavior with evictions + final Cache cache = + Caffeine.newBuilder() + .weigher((String k, String v) -> k.length() + v.length()) + .maximumWeight(35) + .recordStats() + .executor(Runnable::run) + .build(); + + final CacheMetricsCollector collector = new CacheMetricsCollector(); + collector.addCache("users", cache); + + final PrometheusRegistry registry = new PrometheusRegistry(); + registry.register(collector); + + cache.getIfPresent("user1"); + cache.getIfPresent("user1"); + cache.put("user1", "First User"); + cache.getIfPresent("user1"); + + // Add to cache to trigger eviction. + cache.put("user2", "Second User"); + cache.put("user3", "Third User"); + cache.put("user4", "Fourth User"); + + assertCounterMetric(registry, "caffeine_cache_hit", "users", 1.0); + assertCounterMetric(registry, "caffeine_cache_miss", "users", 2.0); + assertCounterMetric(registry, "caffeine_cache_requests", "users", 3.0); + assertCounterMetric(registry, "caffeine_cache_eviction", "users", 2.0); + assertCounterMetric(registry, "caffeine_cache_eviction_weight", "users", 31.0); + + final String expected = + "# TYPE caffeine_cache_estimated_size gauge\n" + + "# HELP caffeine_cache_estimated_size Estimated cache size\n" + + "caffeine_cache_estimated_size{cache=\"users\"} 2.0\n" + + "# TYPE caffeine_cache_eviction counter\n" + + "# HELP caffeine_cache_eviction Cache eviction totals, doesn't include manually removed entries\n" + + "caffeine_cache_eviction_total{cache=\"users\"} 2.0\n" + + "# TYPE caffeine_cache_eviction_weight counter\n" + + "# HELP caffeine_cache_eviction_weight Weight of evicted cache entries, doesn't include manually removed entries\n" + + "caffeine_cache_eviction_weight_total{cache=\"users\"} 31.0\n" + + "# TYPE caffeine_cache_hit counter\n" + + "# HELP caffeine_cache_hit Cache hit totals\n" + + "caffeine_cache_hit_total{cache=\"users\"} 1.0\n" + + "# TYPE caffeine_cache_miss counter\n" + + "# HELP caffeine_cache_miss Cache miss totals\n" + + "caffeine_cache_miss_total{cache=\"users\"} 2.0\n" + + "# TYPE caffeine_cache_requests counter\n" + + "# HELP caffeine_cache_requests Cache request totals, hits + misses\n" + + "caffeine_cache_requests_total{cache=\"users\"} 3.0\n" + + "# TYPE caffeine_cache_weighted_size gauge\n" + + "# HELP caffeine_cache_weighted_size Approximate accumulated weight of cache entries\n" + + "caffeine_cache_weighted_size{cache=\"users\"} 31.0\n" + + "# EOF\n"; + + assertThat(convertToOpenMetricsFormat(registry)).isEqualTo(expected); + } + @SuppressWarnings("unchecked") @Test public void loadingCacheExposesMetricsForLoadsAndExceptions() throws Exception {