package org.stagemonitor.core.metrics.annotations;
import com.codahale.metrics.annotation.ExceptionMetered;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.core.instrument.StagemonitorByteBuddyTransformer;
import org.stagemonitor.core.metrics.metrics2.MetricName;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.List;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static org.stagemonitor.core.metrics.metrics2.MetricName.name;
public class ExceptionMeteredTransformer extends StagemonitorByteBuddyTransformer {
private static final MetricName.MetricNameTemplate metricNameTemplate = name("exception_rate").templateFor("signature");
@Override
protected ElementMatcher.Junction<MethodDescription> getExtraMethodElementMatcher() {
return isAnnotatedWith(ExceptionMetered.class);
}
@Advice.OnMethodExit(onThrowable = Exception.class, inline = false)
public static void meterException(@ExceptionMeteredSignature String signature, @MeterExceptionsFor Class<? extends Exception> cause, @Advice.Thrown Throwable e) {
if (e != null && cause.isInstance(e)) {
Stagemonitor.getMetric2Registry().meter(getMetricName(signature)).mark();
}
}
public static MetricName getMetricName(String signature) {
return metricNameTemplate.build(signature);
}
@Override
protected List<StagemonitorDynamicValue<?>> getDynamicValues() {
return Arrays.asList(new ExceptionMeteredTransformer.ExceptionMeteredSignatureDynamicValue(),
new ExceptionMeteredTransformer.MeterExceptionsForDynamicValue());
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
protected @interface ExceptionMeteredSignature {
}
public static class ExceptionMeteredSignatureDynamicValue extends MetricAnnotationSignatureDynamicValue<ExceptionMeteredSignature> {
@Override
protected NamingParameters getNamingParameters(MethodDescription instrumentedMethod) {
final ExceptionMetered exceptionMetered = instrumentedMethod.getDeclaredAnnotations().ofType(ExceptionMetered.class).loadSilent();
return new NamingParameters(exceptionMetered.name(), exceptionMetered.absolute());
}
@Override
public Class<ExceptionMeteredSignature> getAnnotationClass() {
return ExceptionMeteredSignature.class;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface MeterExceptionsFor {
}
public static class MeterExceptionsForDynamicValue extends StagemonitorDynamicValue<MeterExceptionsFor> {
@Override
protected Object doResolve(TypeDescription instrumentedType,
MethodDescription instrumentedMethod,
ParameterDescription.InDefinedShape target,
AnnotationDescription.Loadable<MeterExceptionsFor> annotation,
Assigner assigner, boolean initialized) {
return new TypeDescription.ForLoadedType(instrumentedMethod.getDeclaredAnnotations().ofType(ExceptionMetered.class).loadSilent().cause());
}
@Override
public Class<MeterExceptionsFor> getAnnotationClass() {
return MeterExceptionsFor.class;
}
}
}