Move cache metrics auto-configuration into spring-boot-cache
This commit is contained in:
committed by
Phillip Webb
parent
ff0876c001
commit
84d01cbad4
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.boot.cache.actuate.metrics.autoconfigure;
|
||||
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.spring.cache.HazelcastCache;
|
||||
import io.micrometer.core.instrument.binder.MeterBinder;
|
||||
import org.cache2k.Cache2kBuilder;
|
||||
import org.cache2k.extra.micrometer.Cache2kCacheMetrics;
|
||||
import org.cache2k.extra.spring.SpringCache2kCache;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.cache.actuate.metrics.Cache2kCacheMeterBinderProvider;
|
||||
import org.springframework.boot.cache.actuate.metrics.CacheMeterBinderProvider;
|
||||
import org.springframework.boot.cache.actuate.metrics.CaffeineCacheMeterBinderProvider;
|
||||
import org.springframework.boot.cache.actuate.metrics.HazelcastCacheMeterBinderProvider;
|
||||
import org.springframework.boot.cache.actuate.metrics.JCacheCacheMeterBinderProvider;
|
||||
import org.springframework.boot.cache.actuate.metrics.RedisCacheMeterBinderProvider;
|
||||
import org.springframework.cache.caffeine.CaffeineCache;
|
||||
import org.springframework.cache.jcache.JCacheCache;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCache;
|
||||
|
||||
/**
|
||||
* Configure {@link CacheMeterBinderProvider} beans.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ MeterBinder.class, CacheMeterBinderProvider.class })
|
||||
class CacheMeterBinderProvidersConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ Cache2kBuilder.class, SpringCache2kCache.class, Cache2kCacheMetrics.class })
|
||||
static class Cache2kCacheMeterBinderProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
Cache2kCacheMeterBinderProvider cache2kCacheMeterBinderProvider() {
|
||||
return new Cache2kCacheMeterBinderProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ CaffeineCache.class, com.github.benmanes.caffeine.cache.Cache.class })
|
||||
static class CaffeineCacheMeterBinderProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
CaffeineCacheMeterBinderProvider caffeineCacheMeterBinderProvider() {
|
||||
return new CaffeineCacheMeterBinderProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ HazelcastCache.class, Hazelcast.class })
|
||||
static class HazelcastCacheMeterBinderProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
HazelcastCacheMeterBinderProvider hazelcastCacheMeterBinderProvider() {
|
||||
return new HazelcastCacheMeterBinderProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ JCacheCache.class, javax.cache.CacheManager.class })
|
||||
static class JCacheCacheMeterBinderProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
JCacheCacheMeterBinderProvider jCacheCacheMeterBinderProvider() {
|
||||
return new JCacheCacheMeterBinderProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RedisCache.class)
|
||||
static class RedisCacheMeterBinderProviderConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisCacheMeterBinderProvider redisCacheMeterBinderProvider() {
|
||||
return new RedisCacheMeterBinderProvider();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.boot.cache.actuate.metrics.autoconfigure;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.cache.autoconfigure.CacheAutoConfiguration;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for metrics on all available
|
||||
* {@link Cache caches}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@AutoConfiguration(after = { CacheAutoConfiguration.class },
|
||||
afterName = "org.springframework.boot.metrics.autoconfigure.CompositeMeterRegistryAutoConfiguration")
|
||||
@ConditionalOnBean(CacheManager.class)
|
||||
@ConditionalOnClass(MeterRegistry.class)
|
||||
@Import({ CacheMeterBinderProvidersConfiguration.class, CacheMetricsRegistrarConfiguration.class })
|
||||
public class CacheMetricsAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.boot.cache.actuate.metrics.autoconfigure;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.SimpleAutowireCandidateResolver;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.cache.actuate.metrics.CacheMeterBinderProvider;
|
||||
import org.springframework.boot.cache.actuate.metrics.CacheMetricsRegistrar;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configure a {@link CacheMetricsRegistrar} and register all available {@link Cache
|
||||
* caches}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean({ CacheMeterBinderProvider.class, MeterRegistry.class })
|
||||
class CacheMetricsRegistrarConfiguration {
|
||||
|
||||
private static final String CACHE_MANAGER_SUFFIX = "cacheManager";
|
||||
|
||||
private final MeterRegistry registry;
|
||||
|
||||
private final CacheMetricsRegistrar cacheMetricsRegistrar;
|
||||
|
||||
private final Map<String, CacheManager> cacheManagers;
|
||||
|
||||
CacheMetricsRegistrarConfiguration(MeterRegistry registry, Collection<CacheMeterBinderProvider<?>> binderProviders,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
this.registry = registry;
|
||||
this.cacheManagers = SimpleAutowireCandidateResolver.resolveAutowireCandidates(beanFactory, CacheManager.class);
|
||||
this.cacheMetricsRegistrar = new CacheMetricsRegistrar(this.registry, binderProviders);
|
||||
bindCachesToRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CacheMetricsRegistrar cacheMetricsRegistrar() {
|
||||
return this.cacheMetricsRegistrar;
|
||||
}
|
||||
|
||||
private void bindCachesToRegistry() {
|
||||
this.cacheManagers.forEach(this::bindCacheManagerToRegistry);
|
||||
}
|
||||
|
||||
private void bindCacheManagerToRegistry(String beanName, CacheManager cacheManager) {
|
||||
cacheManager.getCacheNames()
|
||||
.forEach((cacheName) -> bindCacheToRegistry(beanName, cacheManager.getCache(cacheName)));
|
||||
}
|
||||
|
||||
private void bindCacheToRegistry(String beanName, Cache cache) {
|
||||
Tag cacheManagerTag = Tag.of("cache.manager", getCacheManagerName(beanName));
|
||||
this.cacheMetricsRegistrar.bindCacheToRegistry(cache, cacheManagerTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a {@link CacheManager} based on its {@code beanName}.
|
||||
* @param beanName the name of the {@link CacheManager} bean
|
||||
* @return a name for the given cache manager
|
||||
*/
|
||||
private String getCacheManagerName(String beanName) {
|
||||
if (beanName.length() > CACHE_MANAGER_SUFFIX.length()
|
||||
&& StringUtils.endsWithIgnoreCase(beanName, CACHE_MANAGER_SUFFIX)) {
|
||||
return beanName.substring(0, beanName.length() - CACHE_MANAGER_SUFFIX.length());
|
||||
}
|
||||
return beanName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for cache metrics.
|
||||
*/
|
||||
package org.springframework.boot.cache.actuate.metrics.autoconfigure;
|
||||
@@ -1,2 +1,3 @@
|
||||
org.springframework.boot.cache.actuate.endpoint.autoconfigure.CachesEndpointAutoConfiguration
|
||||
org.springframework.boot.cache.actuate.metrics.autoconfigure.CacheMetricsAutoConfiguration
|
||||
org.springframework.boot.cache.autoconfigure.CacheAutoConfiguration
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.boot.actuate.autoconfigure.metrics.cache;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CaffeineSpec;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.cache.actuate.metrics.autoconfigure.CacheMetricsAutoConfiguration;
|
||||
import org.springframework.boot.cache.autoconfigure.CacheAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CachingConfigurer;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CacheMetricsAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class CacheMetricsAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(SimpleMeterRegistry.class)
|
||||
.withUserConfiguration(CachingConfiguration.class)
|
||||
.withConfiguration(AutoConfigurations.of(CacheAutoConfiguration.class, CacheMetricsAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void autoConfiguredCache2kIsInstrumented() {
|
||||
this.contextRunner.withPropertyValues("spring.cache.type=cache2k", "spring.cache.cache-names=cache1,cache2")
|
||||
.run((context) -> {
|
||||
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||
registry.get("cache.gets").tags("name", "cache1").tags("cache.manager", "cacheManager").meter();
|
||||
registry.get("cache.gets").tags("name", "cache2").tags("cache.manager", "cacheManager").meter();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguredCacheManagerIsInstrumented() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.cache.type=caffeine", "spring.cache.cache-names=cache1,cache2",
|
||||
"spring.cache.caffeine.spec=recordStats")
|
||||
.run((context) -> {
|
||||
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||
registry.get("cache.gets").tags("name", "cache1").tags("cache.manager", "cacheManager").meter();
|
||||
registry.get("cache.gets").tags("name", "cache2").tags("cache.manager", "cacheManager").meter();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfiguredNonSupportedCacheManagerIsIgnored() {
|
||||
this.contextRunner.withPropertyValues("spring.cache.type=simple", "spring.cache.cache-names=cache1,cache2")
|
||||
.run((context) -> {
|
||||
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||
assertThat(registry.find("cache.gets")
|
||||
.tags("name", "cache1")
|
||||
.tags("cache.manager", "cacheManager")
|
||||
.meter()).isNull();
|
||||
assertThat(registry.find("cache.gets")
|
||||
.tags("name", "cache2")
|
||||
.tags("cache.manager", "cacheManager")
|
||||
.meter()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void cacheInstrumentationCanBeDisabled() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.metrics.enable.cache=false", "spring.cache.type=caffeine",
|
||||
"spring.cache.cache-names=cache1")
|
||||
.run((context) -> {
|
||||
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||
assertThat(registry.find("cache.requests")
|
||||
.tags("name", "cache1")
|
||||
.tags("cache.manager", "cacheManager")
|
||||
.meter()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void customCacheManagersAreInstrumented() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.cache.type=caffeine", "spring.cache.cache-names=cache1,cache2",
|
||||
"spring.cache.caffeine.spec=recordStats")
|
||||
.withUserConfiguration(CustomCacheManagersConfiguration.class)
|
||||
.run((context) -> {
|
||||
MeterRegistry registry = context.getBean(MeterRegistry.class);
|
||||
assertThat(registry.find("cache.gets").meters()).map((meter) -> meter.getId().getTag("cache"))
|
||||
.containsOnly("standard", "non-default");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomCacheManagersConfiguration implements CachingConfigurer {
|
||||
|
||||
@Bean
|
||||
CacheManager standardCacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||
cacheManager.setCaffeineSpec(CaffeineSpec.parse("recordStats"));
|
||||
cacheManager.setCacheNames(List.of("standard"));
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean(defaultCandidate = false)
|
||||
CacheManager nonDefaultCacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||
cacheManager.setCaffeineSpec(CaffeineSpec.parse("recordStats"));
|
||||
cacheManager.setCacheNames(List.of("non-default"));
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean(autowireCandidate = false)
|
||||
CacheManager nonAutowireCacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||
cacheManager.setCaffeineSpec(CaffeineSpec.parse("recordStats"));
|
||||
cacheManager.setCacheNames(List.of("non-autowire"));
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public CacheResolver cacheResolver() {
|
||||
return (context) -> Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableCaching
|
||||
static class CachingConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user