001package io.prometheus.client.guava.cache; 002 003import com.google.common.cache.Cache; 004import com.google.common.cache.CacheStats; 005import com.google.common.cache.LoadingCache; 006import io.prometheus.client.Collector; 007import io.prometheus.client.CounterMetricFamily; 008import io.prometheus.client.GaugeMetricFamily; 009import io.prometheus.client.SummaryMetricFamily; 010 011import java.util.ArrayList; 012import java.util.Arrays; 013import java.util.List; 014import java.util.Map; 015import java.util.concurrent.ConcurrentHashMap; 016import java.util.concurrent.ConcurrentMap; 017 018/** 019 * Collect metrics from Guava's com.google.common.cache.Cache. 020 * <p> 021 * <pre>{@code 022 * 023 * // Note that `recordStats()` is required to gather non-zero statistics 024 * Cache<String, String> cache = CacheBuilder.newBuilder().recordStats().build(); 025 * CacheMetricsCollector cacheMetrics = new CacheMetricsCollector().register(); 026 * cacheMetrics.addCache("mycache", cache); 027 * 028 * }</pre> 029 * 030 * Exposed metrics are labeled with the provided cache name. 031 * 032 * With the example above, sample metric names would be: 033 * <pre> 034 * guava_cache_hit_total{cache="mycache"} 10.0 035 * guava_cache_miss_total{cache="mycache"} 3.0 036 * guava_cache_requests_total{cache="mycache"} 13.0 037 * guava_cache_eviction_total{cache="mycache"} 1.0 038 * guava_cache_size{cache="mycache"} 5.0 039 * </pre> 040 * 041 * Additionally if the cache includes a loader, the following metrics would be provided: 042 * <pre> 043 * guava_cache_load_failure_total{cache="mycache"} 2.0 044 * guava_cache_loads_total{cache="mycache"} 7.0 045 * guava_cache_load_duration_seconds_count{cache="mycache"} 7.0 046 * guava_cache_load_duration_seconds_sum{cache="mycache"} 0.0034 047 * </pre> 048 * 049 */ 050public class CacheMetricsCollector extends Collector { 051 052 protected final ConcurrentMap<String, Cache> children = new ConcurrentHashMap<String, Cache>(); 053 054 /** 055 * Add or replace the cache with the given name. 056 * <p> 057 * Any references any previous cache with this name is invalidated. 058 * 059 * @param cacheName The name of the cache, will be the metrics label value 060 * @param cache The cache being monitored 061 */ 062 public void addCache(String cacheName, Cache cache) { 063 children.put(cacheName, cache); 064 } 065 066 /** 067 * Remove the cache with the given name. 068 * <p> 069 * Any references to the cache are invalidated. 070 * 071 * @param cacheName cache to be removed 072 */ 073 public Cache removeCache(String cacheName) { 074 return children.remove(cacheName); 075 } 076 077 /** 078 * Remove all caches. 079 * <p> 080 * Any references to all caches are invalidated. 081 */ 082 public void clear(){ 083 children.clear(); 084 } 085 086 @Override 087 public List<MetricFamilySamples> collect() { 088 List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); 089 List<String> labelNames = Arrays.asList("cache"); 090 091 CounterMetricFamily cacheHitTotal = new CounterMetricFamily("guava_cache_hit_total", 092 "Cache hit totals", labelNames); 093 mfs.add(cacheHitTotal); 094 095 CounterMetricFamily cacheMissTotal = new CounterMetricFamily("guava_cache_miss_total", 096 "Cache miss totals", labelNames); 097 mfs.add(cacheMissTotal); 098 099 CounterMetricFamily cacheRequestsTotal = new CounterMetricFamily("guava_cache_requests_total", 100 "Cache request totals, hits + misses", labelNames); 101 mfs.add(cacheRequestsTotal); 102 103 CounterMetricFamily cacheEvictionTotal = new CounterMetricFamily("guava_cache_eviction_total", 104 "Cache eviction totals, doesn't include manually removed entries", labelNames); 105 mfs.add(cacheEvictionTotal); 106 107 CounterMetricFamily cacheLoadFailure = new CounterMetricFamily("guava_cache_load_failure_total", 108 "Cache load failures", labelNames); 109 mfs.add(cacheLoadFailure); 110 111 CounterMetricFamily cacheLoadTotal = new CounterMetricFamily("guava_cache_loads_total", 112 "Cache loads: both success and failures", labelNames); 113 mfs.add(cacheLoadTotal); 114 115 GaugeMetricFamily cacheSize = new GaugeMetricFamily("guava_cache_size", 116 "Cache size", labelNames); 117 mfs.add(cacheSize); 118 119 SummaryMetricFamily cacheLoadSummary = new SummaryMetricFamily("guava_cache_load_duration_seconds", 120 "Cache load duration: both success and failures", labelNames); 121 mfs.add(cacheLoadSummary); 122 123 for(Map.Entry<String, Cache> c: children.entrySet()) { 124 List<String> cacheName = Arrays.asList(c.getKey()); 125 CacheStats stats = c.getValue().stats(); 126 127 cacheHitTotal.addMetric(cacheName, stats.hitCount()); 128 cacheMissTotal.addMetric(cacheName, stats.missCount()); 129 cacheRequestsTotal.addMetric(cacheName, stats.requestCount()); 130 cacheEvictionTotal.addMetric(cacheName, stats.evictionCount()); 131 cacheSize.addMetric(cacheName, c.getValue().size()); 132 133 if(c.getValue() instanceof LoadingCache) { 134 cacheLoadFailure.addMetric(cacheName, stats.loadExceptionCount()); 135 cacheLoadTotal.addMetric(cacheName, stats.loadCount()); 136 137 cacheLoadSummary.addMetric(cacheName, stats.loadCount(), stats.totalLoadTime() / Collector.NANOSECONDS_PER_SECOND); 138 } 139 } 140 return mfs; 141 } 142}