package org.stagemonitor.core.metrics.annotations; import com.codahale.metrics.annotation.Gauge; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.stagemonitor.core.Stagemonitor; import org.stagemonitor.core.instrument.StagemonitorByteBuddyTransformer; import org.stagemonitor.core.metrics.MonitorGauges; import org.stagemonitor.core.metrics.aspects.SignatureUtils; import java.lang.reflect.Method; import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static org.stagemonitor.core.metrics.metrics2.MetricName.name; /** * Implementation for the {@link Gauge} annotation */ public class GaugeTransformer extends StagemonitorByteBuddyTransformer { private static final Logger logger = LoggerFactory.getLogger(GaugeTransformer.class); @Override protected ElementMatcher.Junction<TypeDescription> getNarrowTypesMatcher() { return isAnnotatedWith(MonitorGauges.class); } @Override protected ElementMatcher.Junction<MethodDescription> getMethodElementMatcher() { return isConstructor(); } @Advice.OnMethodExit public static void gauges(@Advice.This Object thiz) { monitorGauges(thiz); } public static void monitorGauges(Object object) { for (final Method method : object.getClass().getDeclaredMethods()) { final Gauge gaugeAnnotation = method.getAnnotation(Gauge.class); // only create gauge, if method takes no parameters and is non-void if (gaugeAnnotation != null && methodTakesNoParamsAndIsNonVoid(method)) { method.setAccessible(true); final String signature = SignatureUtils.getSignature(object.getClass().getName(), method.getName(), gaugeAnnotation.name(), gaugeAnnotation.absolute()); registerGauge(object, method, signature); } } } private static boolean methodTakesNoParamsAndIsNonVoid(Method method) { return method.getGenericParameterTypes().length == 0 && method.getReturnType() != Void.class; } private static void registerGauge(final Object object, final Method method, final String signature) { Stagemonitor.getMetric2Registry().registerNewMetrics(name("gauge_" + signature).build(), new com.codahale.metrics.Gauge() { @Override public Object getValue() { try { return method.invoke(object); } catch (Exception e) { logger.warn("Error occurred while invoking gauge {}: {}", signature, e.getMessage()); return null; } } }); } }