From ec56b2c43b72f5b998ad11919911686afe49408d Mon Sep 17 00:00:00 2001 From: Petar Heyken <48122009+pheyken@users.noreply.github.com> Date: Sat, 31 Aug 2024 03:14:05 +0200 Subject: [PATCH] add support for caffeine instrumentation with 1.X.X (#983) * add support for caffeine instrumentation with 1.X.X --------- Signed-off-by: Petar Heyken --- pom.xml | 1 + .../pom.xml | 34 ++- .../caffeine/CacheMetricsCollector.java | 232 ++++++++++++++++++ .../caffeine/CacheMetricsCollectorTest.java | 153 ++++++++++++ .../version-rules.xml | 0 .../cache/caffeine/CacheMetricsCollector.java | 165 ------------- .../caffeine/CacheMetricsCollectorTest.java | 93 ------- 7 files changed, 412 insertions(+), 266 deletions(-) rename {simpleclient-archive/simpleclient_caffeine => prometheus-metrics-instrumentation-caffeine}/pom.xml (64%) create mode 100644 prometheus-metrics-instrumentation-caffeine/src/main/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollector.java create mode 100644 prometheus-metrics-instrumentation-caffeine/src/test/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollectorTest.java rename {simpleclient-archive/simpleclient_caffeine => prometheus-metrics-instrumentation-caffeine}/version-rules.xml (100%) delete mode 100644 simpleclient-archive/simpleclient_caffeine/src/main/java/io/prometheus/client/cache/caffeine/CacheMetricsCollector.java delete mode 100644 simpleclient-archive/simpleclient_caffeine/src/test/java/io/prometheus/client/cache/caffeine/CacheMetricsCollectorTest.java diff --git a/pom.xml b/pom.xml index 033e89331..52953fbc9 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ prometheus-metrics-exporter-httpserver prometheus-metrics-exporter-opentelemetry prometheus-metrics-exporter-pushgateway + prometheus-metrics-instrumentation-caffeine prometheus-metrics-instrumentation-jvm prometheus-metrics-instrumentation-dropwizard5 prometheus-metrics-instrumentation-guava diff --git a/simpleclient-archive/simpleclient_caffeine/pom.xml b/prometheus-metrics-instrumentation-caffeine/pom.xml similarity index 64% rename from simpleclient-archive/simpleclient_caffeine/pom.xml rename to prometheus-metrics-instrumentation-caffeine/pom.xml index 73529822b..a1b560251 100644 --- a/simpleclient-archive/simpleclient_caffeine/pom.xml +++ b/prometheus-metrics-instrumentation-caffeine/pom.xml @@ -5,17 +5,21 @@ io.prometheus client_java - 1.0.0-beta-2-SNAPSHOT + 1.4.0-SNAPSHOT - simpleclient_caffeine + prometheus-metrics-instrumentation-caffeine bundle - Prometheus Java Simpleclient Caffeine + Prometheus Metrics Instrumentation - Caffeine - Metrics collector for caffeine based caches + Instrumentation library for caffeine based caches + + io.prometheus.metrics.instrumentation.caffeine + + The Apache Software License, Version 2.0 @@ -30,18 +34,25 @@ Clint Checketts checketts@gmail.com + + + pheyken + Petar Heyken + mail@petar-heyken.de + io.prometheus - simpleclient + prometheus-metrics-core ${project.version} com.github.ben-manes.caffeine caffeine - 3.1.1 + 3.1.8 + provided @@ -55,13 +66,20 @@ org.mockito mockito-core - 4.6.1 + 5.12.0 test org.assertj assertj-core - 3.23.1 + 3.26.3 + test + + + + io.prometheus + prometheus-metrics-exposition-formats + ${project.version} test 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 new file mode 100644 index 000000000..985a15711 --- /dev/null +++ b/prometheus-metrics-instrumentation-caffeine/src/main/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollector.java @@ -0,0 +1,232 @@ +package io.prometheus.metrics.instrumentation.caffeine; + +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.stats.CacheStats; +import io.prometheus.metrics.model.registry.MultiCollector; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.GaugeSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import io.prometheus.metrics.model.snapshots.SummarySnapshot; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + + +/** + * Collect metrics from Caffeine's com.github.benmanes.caffeine.cache.Cache. + *

+ *

{@code
+ *
+ * // Note that `recordStats()` is required to gather non-zero statistics
+ * Cache cache = Caffeine.newBuilder().recordStats().build();
+ * CacheMetricsCollector cacheMetrics = new CacheMetricsCollector().register();
+ * cacheMetrics.addCache("mycache", cache);
+ *
+ * }
+ * + * Exposed metrics are labeled with the provided cache name. + * + * With the example above, sample metric names would be: + *
+ *     caffeine_cache_hit_total{cache="mycache"} 10.0
+ *     caffeine_cache_miss_total{cache="mycache"} 3.0
+ *     caffeine_cache_requests_total{cache="mycache"} 13.0
+ *     caffeine_cache_eviction_total{cache="mycache"} 1.0
+ *     caffeine_cache_estimated_size{cache="mycache"} 5.0
+ * 
+ * + * Additionally, if the cache includes a loader, the following metrics would be provided: + *
+ *     caffeine_cache_load_failure_total{cache="mycache"} 2.0
+ *     caffeine_cache_loads_total{cache="mycache"} 7.0
+ *     caffeine_cache_load_duration_seconds_count{cache="mycache"} 7.0
+ *     caffeine_cache_load_duration_seconds_sum{cache="mycache"} 0.0034
+ * 
+ * + */ +public class CacheMetricsCollector implements MultiCollector { + private static final double NANOSECONDS_PER_SECOND = 1_000_000_000.0; + + protected final ConcurrentMap children = new ConcurrentHashMap(); + + /** + * Add or replace the cache with the given name. + *

+ * Any references any previous cache with this name is invalidated. + * + * @param cacheName The name of the cache, will be the metrics label value + * @param cache The cache being monitored + */ + public void addCache(String cacheName, Cache cache) { + children.put(cacheName, cache); + } + + /** + * Add or replace the cache with the given name. + *

+ * Any references any previous cache with this name is invalidated. + * + * @param cacheName The name of the cache, will be the metrics label value + * @param cache The cache being monitored + */ + public void addCache(String cacheName, AsyncCache cache) { + children.put(cacheName, cache.synchronous()); + } + + /** + * Remove the cache with the given name. + *

+ * Any references to the cache are invalidated. + * + * @param cacheName cache to be removed + */ + public Cache removeCache(String cacheName) { + return children.remove(cacheName); + } + + /** + * Remove all caches. + *

+ * Any references to all caches are invalidated. + */ + public void clear(){ + children.clear(); + } + + @Override + public MetricSnapshots collect() { + final MetricSnapshots.Builder metricSnapshotsBuilder = MetricSnapshots.builder(); + final List labelNames = Arrays.asList("cache"); + + final CounterSnapshot.Builder cacheHitTotal = CounterSnapshot.builder() + .name("caffeine_cache_hit") + .help("Cache hit totals"); + + final CounterSnapshot.Builder cacheMissTotal = CounterSnapshot.builder() + .name("caffeine_cache_miss") + .help("Cache miss totals"); + + final CounterSnapshot.Builder cacheRequestsTotal = CounterSnapshot.builder() + .name("caffeine_cache_requests") + .help("Cache request totals, hits + misses"); + + final CounterSnapshot.Builder cacheEvictionTotal = CounterSnapshot.builder() + .name("caffeine_cache_eviction") + .help("Cache eviction totals, doesn't include manually removed entries"); + + final GaugeSnapshot.Builder cacheEvictionWeight = GaugeSnapshot.builder() + .name("caffeine_cache_eviction_weight") + .help("Cache eviction weight"); + + final CounterSnapshot.Builder cacheLoadFailure = CounterSnapshot.builder() + .name("caffeine_cache_load_failure") + .help("Cache load failures"); + + final CounterSnapshot.Builder cacheLoadTotal = CounterSnapshot.builder() + .name("caffeine_cache_loads") + .help("Cache loads: both success and failures"); + + final GaugeSnapshot.Builder cacheSize = GaugeSnapshot.builder() + .name("caffeine_cache_estimated_size") + .help("Estimated cache size"); + + final SummarySnapshot.Builder cacheLoadSummary = SummarySnapshot.builder() + .name("caffeine_cache_load_duration_seconds") + .help("Cache load duration: both success and failures"); + + for (final Map.Entry c: children.entrySet()) { + final List cacheName = Arrays.asList(c.getKey()); + final Labels labels = Labels.of(labelNames, cacheName); + + final CacheStats stats = c.getValue().stats(); + + try { + cacheEvictionWeight.dataPoint( + GaugeSnapshot.GaugeDataPointSnapshot.builder() + .labels(labels) + .value(stats.evictionWeight()) + .build() + ); + } catch (Exception e) { + // EvictionWeight metric is unavailable, newer version of Caffeine is needed. + } + + cacheHitTotal.dataPoint( + CounterSnapshot.CounterDataPointSnapshot.builder() + .labels(labels) + .value(stats.hitCount()) + .build() + ); + + cacheMissTotal.dataPoint( + CounterSnapshot.CounterDataPointSnapshot.builder() + .labels(labels) + .value(stats.missCount()) + .build() + ); + + cacheRequestsTotal.dataPoint( + CounterSnapshot.CounterDataPointSnapshot.builder() + .labels(labels) + .value(stats.requestCount()) + .build() + ); + + cacheEvictionTotal.dataPoint( + CounterSnapshot.CounterDataPointSnapshot.builder() + .labels(labels) + .value(stats.evictionCount()) + .build() + ); + + cacheSize.dataPoint( + GaugeSnapshot.GaugeDataPointSnapshot.builder() + .labels(labels) + .value(c.getValue().estimatedSize()) + .build() + ); + + if (c.getValue() instanceof LoadingCache) { + cacheLoadFailure.dataPoint( + CounterSnapshot.CounterDataPointSnapshot.builder() + .labels(labels) + .value(stats.loadFailureCount()) + .build() + ); + + cacheLoadTotal.dataPoint( + CounterSnapshot.CounterDataPointSnapshot.builder() + .labels(labels) + .value(stats.loadCount()) + .build() + ); + + cacheLoadSummary.dataPoint( + SummarySnapshot.SummaryDataPointSnapshot.builder() + .labels(labels) + .count(stats.loadCount()) + .sum(stats.totalLoadTime() / NANOSECONDS_PER_SECOND) + .build() + ); + } + } + + return metricSnapshotsBuilder + .metricSnapshot(cacheHitTotal.build()) + .metricSnapshot(cacheMissTotal.build()) + .metricSnapshot(cacheRequestsTotal.build()) + .metricSnapshot(cacheEvictionTotal.build()) + .metricSnapshot(cacheEvictionWeight.build()) + .metricSnapshot(cacheLoadFailure.build()) + .metricSnapshot(cacheLoadTotal.build()) + .metricSnapshot(cacheSize.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 new file mode 100644 index 000000000..86f7ed185 --- /dev/null +++ b/prometheus-metrics-instrumentation-caffeine/src/test/java/io/prometheus/metrics/instrumentation/caffeine/CacheMetricsCollectorTest.java @@ -0,0 +1,153 @@ +package io.prometheus.metrics.instrumentation.caffeine; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.CacheLoader; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import io.prometheus.metrics.expositionformats.OpenMetricsTextFormatWriter; +import io.prometheus.metrics.model.registry.PrometheusRegistry; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.DataPointSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.SummarySnapshot; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Executor; + +import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CacheMetricsCollectorTest { + + @Test + public void cacheExposesMetricsForHitMissAndEviction() { + final Cache cache = Caffeine.newBuilder().maximumSize(2).recordStats().executor(new Executor() { + @Override + public void execute(Runnable command) { + // Run cleanup in same thread, to remove async behavior with evictions + command.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); + + 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 gauge\n" + + "# HELP caffeine_cache_eviction_weight Cache eviction weight\n" + + "caffeine_cache_eviction_weight{cache=\"users\"} 2.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" + + "# EOF\n"; + + assertEquals(expected, convertToOpenMetricsFormat(registry)); + } + + @SuppressWarnings("unchecked") + @Test + public void loadingCacheExposesMetricsForLoadsAndExceptions() throws Exception { + final CacheLoader loader = mock(CacheLoader.class); + when(loader.load(anyString())) + .thenReturn("First User") + .thenThrow(new RuntimeException("Seconds time fails")) + .thenReturn("Third User"); + + final LoadingCache cache = Caffeine.newBuilder().recordStats().build(loader); + final CacheMetricsCollector collector = new CacheMetricsCollector(); + + collector.addCache("loadingusers", cache); + + final PrometheusRegistry registry = new PrometheusRegistry(); + registry.register(collector); + + cache.get("user1"); + cache.get("user1"); + try { + cache.get("user2"); + } catch (Exception e) { + // ignoring. + } + cache.get("user3"); + + assertCounterMetric(registry, "caffeine_cache_hit", "loadingusers", 1.0); + assertCounterMetric(registry, "caffeine_cache_miss", "loadingusers", 3.0); + + assertCounterMetric(registry, "caffeine_cache_load_failure", "loadingusers", 1.0); + assertCounterMetric(registry, "caffeine_cache_loads", "loadingusers", 3.0); + + final SummarySnapshot.SummaryDataPointSnapshot loadDuration = (SummarySnapshot.SummaryDataPointSnapshot) getDataPointSnapshot( + registry, + "caffeine_cache_load_duration_seconds", + "loadingusers" + ); + + assertEquals(3, loadDuration.getCount()); + assertThat(loadDuration.getSum()).isGreaterThan(0); + } + + private void assertCounterMetric(PrometheusRegistry registry, String name, String cacheName, double value) { + final CounterSnapshot.CounterDataPointSnapshot dataPointSnapshot = + (CounterSnapshot.CounterDataPointSnapshot) getDataPointSnapshot(registry, name, cacheName); + + assertEquals(value, dataPointSnapshot.getValue(), 0); + } + + private DataPointSnapshot getDataPointSnapshot(PrometheusRegistry registry, String name, String cacheName) + { + final Labels labels = Labels.of(new String[]{"cache"}, new String[]{cacheName}); + + return registry.scrape(name::equals).stream() + .flatMap(metricSnapshot -> metricSnapshot.getDataPoints().stream()) + .filter(dataPoint -> dataPoint.getLabels().equals(labels)) + .findFirst() + .get(); + } + + private String convertToOpenMetricsFormat(PrometheusRegistry registry) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final OpenMetricsTextFormatWriter writer = new OpenMetricsTextFormatWriter(true, true); + try { + writer.write(out, registry.scrape()); + return out.toString(StandardCharsets.UTF_8.name()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/simpleclient-archive/simpleclient_caffeine/version-rules.xml b/prometheus-metrics-instrumentation-caffeine/version-rules.xml similarity index 100% rename from simpleclient-archive/simpleclient_caffeine/version-rules.xml rename to prometheus-metrics-instrumentation-caffeine/version-rules.xml diff --git a/simpleclient-archive/simpleclient_caffeine/src/main/java/io/prometheus/client/cache/caffeine/CacheMetricsCollector.java b/simpleclient-archive/simpleclient_caffeine/src/main/java/io/prometheus/client/cache/caffeine/CacheMetricsCollector.java deleted file mode 100644 index bc48dd86f..000000000 --- a/simpleclient-archive/simpleclient_caffeine/src/main/java/io/prometheus/client/cache/caffeine/CacheMetricsCollector.java +++ /dev/null @@ -1,165 +0,0 @@ -package io.prometheus.client.cache.caffeine; - -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.stats.CacheStats; -import io.prometheus.client.Collector; -import io.prometheus.client.CounterMetricFamily; -import io.prometheus.client.GaugeMetricFamily; -import io.prometheus.client.SummaryMetricFamily; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - - -/** - * Collect metrics from Caffeine's com.github.benmanes.caffeine.cache.Cache. - *

- *

{@code
- *
- * // Note that `recordStats()` is required to gather non-zero statistics
- * Cache cache = Caffeine.newBuilder().recordStats().build();
- * CacheMetricsCollector cacheMetrics = new CacheMetricsCollector().register();
- * cacheMetrics.addCache("mycache", cache);
- *
- * }
- * - * Exposed metrics are labeled with the provided cache name. - * - * With the example above, sample metric names would be: - *
- *     caffeine_cache_hit_total{cache="mycache"} 10.0
- *     caffeine_cache_miss_total{cache="mycache"} 3.0
- *     caffeine_cache_requests_total{cache="mycache"} 13.0
- *     caffeine_cache_eviction_total{cache="mycache"} 1.0
- *     caffeine_cache_estimated_size{cache="mycache"} 5.0
- * 
- * - * Additionally if the cache includes a loader, the following metrics would be provided: - *
- *     caffeine_cache_load_failure_total{cache="mycache"} 2.0
- *     caffeine_cache_loads_total{cache="mycache"} 7.0
- *     caffeine_cache_load_duration_seconds_count{cache="mycache"} 7.0
- *     caffeine_cache_load_duration_seconds_sum{cache="mycache"} 0.0034
- * 
- * - */ -public class CacheMetricsCollector extends Collector { - protected final ConcurrentMap children = new ConcurrentHashMap(); - - /** - * Add or replace the cache with the given name. - *

- * Any references any previous cache with this name is invalidated. - * - * @param cacheName The name of the cache, will be the metrics label value - * @param cache The cache being monitored - */ - public void addCache(String cacheName, Cache cache) { - children.put(cacheName, cache); - } - - /** - * Add or replace the cache with the given name. - *

- * Any references any previous cache with this name is invalidated. - * - * @param cacheName The name of the cache, will be the metrics label value - * @param cache The cache being monitored - */ - public void addCache(String cacheName, AsyncCache cache) { - children.put(cacheName, cache.synchronous()); - } - - /** - * Remove the cache with the given name. - *

- * Any references to the cache are invalidated. - * - * @param cacheName cache to be removed - */ - public Cache removeCache(String cacheName) { - return children.remove(cacheName); - } - - /** - * Remove all caches. - *

- * Any references to all caches are invalidated. - */ - public void clear(){ - children.clear(); - } - - @Override - public List collect() { - List mfs = new ArrayList(); - List labelNames = Arrays.asList("cache"); - - CounterMetricFamily cacheHitTotal = new CounterMetricFamily("caffeine_cache_hit_total", - "Cache hit totals", labelNames); - mfs.add(cacheHitTotal); - - CounterMetricFamily cacheMissTotal = new CounterMetricFamily("caffeine_cache_miss_total", - "Cache miss totals", labelNames); - mfs.add(cacheMissTotal); - - CounterMetricFamily cacheRequestsTotal = new CounterMetricFamily("caffeine_cache_requests_total", - "Cache request totals, hits + misses", labelNames); - mfs.add(cacheRequestsTotal); - - CounterMetricFamily cacheEvictionTotal = new CounterMetricFamily("caffeine_cache_eviction_total", - "Cache eviction totals, doesn't include manually removed entries", labelNames); - mfs.add(cacheEvictionTotal); - - GaugeMetricFamily cacheEvictionWeight = new GaugeMetricFamily("caffeine_cache_eviction_weight", - "Cache eviction weight", labelNames); - mfs.add(cacheEvictionWeight); - - CounterMetricFamily cacheLoadFailure = new CounterMetricFamily("caffeine_cache_load_failure_total", - "Cache load failures", labelNames); - mfs.add(cacheLoadFailure); - - CounterMetricFamily cacheLoadTotal = new CounterMetricFamily("caffeine_cache_loads_total", - "Cache loads: both success and failures", labelNames); - mfs.add(cacheLoadTotal); - - GaugeMetricFamily cacheSize = new GaugeMetricFamily("caffeine_cache_estimated_size", - "Estimated cache size", labelNames); - mfs.add(cacheSize); - - SummaryMetricFamily cacheLoadSummary = new SummaryMetricFamily("caffeine_cache_load_duration_seconds", - "Cache load duration: both success and failures", labelNames); - mfs.add(cacheLoadSummary); - - for(Map.Entry c: children.entrySet()) { - List cacheName = Arrays.asList(c.getKey()); - CacheStats stats = c.getValue().stats(); - - try{ - cacheEvictionWeight.addMetric(cacheName, stats.evictionWeight()); - } catch (Exception e) { - // EvictionWeight metric is unavailable, newer version of Caffeine is needed. - } - - cacheHitTotal.addMetric(cacheName, stats.hitCount()); - cacheMissTotal.addMetric(cacheName, stats.missCount()); - cacheRequestsTotal.addMetric(cacheName, stats.requestCount()); - cacheEvictionTotal.addMetric(cacheName, stats.evictionCount()); - cacheSize.addMetric(cacheName, c.getValue().estimatedSize()); - - if(c.getValue() instanceof LoadingCache) { - cacheLoadFailure.addMetric(cacheName, stats.loadFailureCount()); - cacheLoadTotal.addMetric(cacheName, stats.loadCount()); - - cacheLoadSummary.addMetric(cacheName, stats.loadCount(), stats.totalLoadTime() / Collector.NANOSECONDS_PER_SECOND); - } - } - return mfs; - } -} diff --git a/simpleclient-archive/simpleclient_caffeine/src/test/java/io/prometheus/client/cache/caffeine/CacheMetricsCollectorTest.java b/simpleclient-archive/simpleclient_caffeine/src/test/java/io/prometheus/client/cache/caffeine/CacheMetricsCollectorTest.java deleted file mode 100644 index 65429f337..000000000 --- a/simpleclient-archive/simpleclient_caffeine/src/test/java/io/prometheus/client/cache/caffeine/CacheMetricsCollectorTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.prometheus.client.cache.caffeine; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.CacheLoader; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import io.prometheus.client.CollectorRegistry; -import org.junit.Test; - -import java.util.concurrent.Executor; - -import static org.assertj.core.api.Java6Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CacheMetricsCollectorTest { - - @Test - public void cacheExposesMetricsForHitMissAndEviction() throws Exception { - Cache cache = Caffeine.newBuilder().maximumSize(2).recordStats().executor(new Executor() { - @Override - public void execute(Runnable command) { - // Run cleanup in same thread, to remove async behavior with evictions - command.run(); - } - }).build(); - CollectorRegistry registry = new CollectorRegistry(); - - CacheMetricsCollector collector = new CacheMetricsCollector().register(registry); - collector.addCache("users", cache); - - 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"); - - assertMetric(registry, "caffeine_cache_hit_total", "users", 1.0); - assertMetric(registry, "caffeine_cache_miss_total", "users", 2.0); - assertMetric(registry, "caffeine_cache_requests_total", "users", 3.0); - assertMetric(registry, "caffeine_cache_eviction_total", "users", 2.0); - } - - - @SuppressWarnings("unchecked") - @Test - public void loadingCacheExposesMetricsForLoadsAndExceptions() throws Exception { - CacheLoader loader = mock(CacheLoader.class); - when(loader.load(anyString())) - .thenReturn("First User") - .thenThrow(new RuntimeException("Seconds time fails")) - .thenReturn("Third User"); - - LoadingCache cache = Caffeine.newBuilder().recordStats().build(loader); - CollectorRegistry registry = new CollectorRegistry(); - CacheMetricsCollector collector = new CacheMetricsCollector().register(registry); - collector.addCache("loadingusers", cache); - - cache.get("user1"); - cache.get("user1"); - try { - cache.get("user2"); - } catch (Exception e) { - // ignoring. - } - cache.get("user3"); - - assertMetric(registry, "caffeine_cache_hit_total", "loadingusers", 1.0); - assertMetric(registry, "caffeine_cache_miss_total", "loadingusers", 3.0); - - assertMetric(registry, "caffeine_cache_load_failure_total", "loadingusers", 1.0); - assertMetric(registry, "caffeine_cache_loads_total", "loadingusers", 3.0); - - assertMetric(registry, "caffeine_cache_load_duration_seconds_count", "loadingusers", 3.0); - assertMetricGreatThan(registry, "caffeine_cache_load_duration_seconds_sum", "loadingusers", 0.0); - } - - private void assertMetric(CollectorRegistry registry, String name, String cacheName, double value) { - assertThat(registry.getSampleValue(name, new String[]{"cache"}, new String[]{cacheName})).isEqualTo(value); - } - - - private void assertMetricGreatThan(CollectorRegistry registry, String name, String cacheName, double value) { - assertThat(registry.getSampleValue(name, new String[]{"cache"}, new String[]{cacheName})).isGreaterThan(value); - } - - -}