001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.actuate.metrics.cache; 018 019import java.util.Collection; 020import java.util.Objects; 021 022import io.micrometer.core.instrument.MeterRegistry; 023import io.micrometer.core.instrument.Tag; 024import io.micrometer.core.instrument.Tags; 025import io.micrometer.core.instrument.binder.MeterBinder; 026 027import org.springframework.boot.util.LambdaSafe; 028import org.springframework.cache.Cache; 029import org.springframework.cache.transaction.TransactionAwareCacheDecorator; 030import org.springframework.util.ClassUtils; 031 032/** 033 * Register supported {@link Cache} to a {@link MeterRegistry}. 034 * 035 * @author Stephane Nicoll 036 * @since 2.0.0 037 */ 038public class CacheMetricsRegistrar { 039 040 private final MeterRegistry registry; 041 042 private final Collection<CacheMeterBinderProvider<?>> binderProviders; 043 044 /** 045 * Creates a new registrar. 046 * @param registry the {@link MeterRegistry} to use 047 * @param binderProviders the {@link CacheMeterBinderProvider} instances that should 048 * be used to detect compatible caches 049 */ 050 public CacheMetricsRegistrar(MeterRegistry registry, 051 Collection<CacheMeterBinderProvider<?>> binderProviders) { 052 this.registry = registry; 053 this.binderProviders = binderProviders; 054 } 055 056 /** 057 * Attempt to bind the specified {@link Cache} to the registry. Return {@code true} if 058 * the cache is supported and was bound to the registry, {@code false} otherwise. 059 * @param cache the cache to handle 060 * @param tags the tags to associate with the metrics of that cache 061 * @return {@code true} if the {@code cache} is supported and was registered 062 */ 063 public boolean bindCacheToRegistry(Cache cache, Tag... tags) { 064 MeterBinder meterBinder = getMeterBinder(unwrapIfNecessary(cache), Tags.of(tags)); 065 if (meterBinder != null) { 066 meterBinder.bindTo(this.registry); 067 return true; 068 } 069 return false; 070 } 071 072 @SuppressWarnings({ "unchecked" }) 073 private MeterBinder getMeterBinder(Cache cache, Tags tags) { 074 Tags cacheTags = tags.and(getAdditionalTags(cache)); 075 return LambdaSafe 076 .callbacks(CacheMeterBinderProvider.class, this.binderProviders, cache) 077 .withLogger(CacheMetricsRegistrar.class) 078 .invokeAnd((binderProvider) -> binderProvider.getMeterBinder(cache, 079 cacheTags)) 080 .filter(Objects::nonNull).findFirst().orElse(null); 081 } 082 083 /** 084 * Return additional {@link Tag tags} to be associated with the given {@link Cache}. 085 * @param cache the cache 086 * @return a list of additional tags to associate to that {@code cache}. 087 */ 088 protected Iterable<Tag> getAdditionalTags(Cache cache) { 089 return Tags.of("name", cache.getName()); 090 } 091 092 private Cache unwrapIfNecessary(Cache cache) { 093 if (ClassUtils.isPresent( 094 "org.springframework.cache.transaction.TransactionAwareCacheDecorator", 095 getClass().getClassLoader())) { 096 return TransactionAwareCacheDecoratorHandler.unwrapIfNecessary(cache); 097 } 098 return cache; 099 } 100 101 private static class TransactionAwareCacheDecoratorHandler { 102 103 private static Cache unwrapIfNecessary(Cache cache) { 104 try { 105 if (cache instanceof TransactionAwareCacheDecorator) { 106 return ((TransactionAwareCacheDecorator) cache).getTargetCache(); 107 } 108 } 109 catch (NoClassDefFoundError ex) { 110 // Ignore 111 } 112 return cache; 113 } 114 115 } 116 117}