All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.core.instrument.binder.cache.CaffeineStatsCounter Maven / Gradle / Ivy

/*
 * Copyright 2021 VMware, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micrometer.core.instrument.binder.cache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import com.github.benmanes.caffeine.cache.stats.StatsCounter;
import io.micrometer.core.instrument.*;
import io.micrometer.core.lang.NonNullApi;
import io.micrometer.core.lang.NonNullFields;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.concurrent.TimeUnit;

import static java.util.Objects.requireNonNull;

/**
 * A {@link StatsCounter} instrumented with Micrometer. This will provide more detailed
 * metrics than using {@link CaffeineCacheMetrics}.
 * 

* Note that this doesn't instrument the cache's size by default. Use * {@link #registerSizeMetric(Cache)} to do so after the cache has been built. *

* Use {@link com.github.benmanes.caffeine.cache.Caffeine#recordStats} to supply this * class to the cache builder:

{@code
 * MeterRegistry registry = ...;
 * Cache graphs = Caffeine.newBuilder()
 *     .maximumSize(10_000)
 *     .recordStats(() -> new CaffeineStatsCounter(registry, "graphs"))
 *     .build();
 * }
* * @author Ben Manes * @author John Karp * @author Johnny Lim * @see CaffeineCacheMetrics * @since 1.7.0 */ @NonNullApi @NonNullFields public final class CaffeineStatsCounter implements StatsCounter { private static final String DESCRIPTION_CACHE_GETS = "The number of times cache lookup methods have returned a cached (hit) or uncached (newly loaded) value (miss)."; private static final String DESCRIPTION_CACHE_LOADS = "The number of times cache lookup methods have successfully loaded a new value or failed to load a new value, either because no value was found or an exception was thrown while loading"; private final MeterRegistry registry; private final Tags tags; private final Counter hitCount; private final Counter missCount; private final Timer loadSuccesses; private final Timer loadFailures; private final EnumMap evictionMetrics; /** * Constructs an instance for use by a single cache. * @param registry the registry of metric instances * @param cacheName will be used to tag metrics with "cache". */ public CaffeineStatsCounter(MeterRegistry registry, String cacheName) { this(registry, cacheName, Tags.empty()); } /** * Constructs an instance for use by a single cache. * @param registry the registry of metric instances * @param cacheName will be used to tag metrics with "cache". * @param extraTags tags to apply to all recorded metrics. */ public CaffeineStatsCounter(MeterRegistry registry, String cacheName, Iterable extraTags) { requireNonNull(registry); requireNonNull(cacheName); requireNonNull(extraTags); this.registry = registry; this.tags = Tags.concat(extraTags, "cache", cacheName); hitCount = Counter.builder("cache.gets") .tag("result", "hit") .tags(tags) .description(DESCRIPTION_CACHE_GETS) .register(registry); missCount = Counter.builder("cache.gets") .tag("result", "miss") .tags(tags) .description(DESCRIPTION_CACHE_GETS) .register(registry); loadSuccesses = Timer.builder("cache.loads") .tag("result", "success") .tags(tags) .description(DESCRIPTION_CACHE_LOADS) .register(registry); loadFailures = Timer.builder("cache.loads") .tag("result", "failure") .tags(tags) .description(DESCRIPTION_CACHE_LOADS) .register(registry); evictionMetrics = new EnumMap<>(RemovalCause.class); Arrays.stream(RemovalCause.values()) .forEach(cause -> evictionMetrics.put(cause, DistributionSummary.builder("cache.evictions") .tag("cause", cause.name()) .tags(tags) .description("The number of times the cache was evicted.") .register(registry))); } /** * Register a gauge for the size of the given cache. * @param cache cache to register a gauge for its size */ public void registerSizeMetric(Cache cache) { Gauge.builder("cache.size", cache, Cache::estimatedSize) .tags(tags) .description("The approximate number of entries in this cache.") .register(registry); } @Override public void recordHits(int count) { hitCount.increment(count); } @Override public void recordMisses(int count) { missCount.increment(count); } @Override public void recordLoadSuccess(long loadTime) { loadSuccesses.record(loadTime, TimeUnit.NANOSECONDS); } @Override public void recordLoadFailure(long loadTime) { loadFailures.record(loadTime, TimeUnit.NANOSECONDS); } @SuppressWarnings("deprecation") public void recordEviction() { } @Override public void recordEviction(int weight, RemovalCause cause) { evictionMetrics.get(cause).record(weight); } @Override public CacheStats snapshot() { return CacheStats.of((long) hitCount.count(), (long) missCount.count(), loadSuccesses.count(), loadFailures.count(), (long) loadSuccesses.totalTime(TimeUnit.NANOSECONDS) + (long) loadFailures.totalTime(TimeUnit.NANOSECONDS), evictionMetrics.values().stream().mapToLong(DistributionSummary::count).sum(), (long) evictionMetrics.values().stream().mapToDouble(DistributionSummary::totalAmount).sum()); } @Override public String toString() { return snapshot().toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy