diff --git a/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedParameter.java b/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedObject.java similarity index 86% rename from micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedParameter.java rename to micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedObject.java index fa21bfd53a..2e1d202ecd 100644 --- a/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedParameter.java +++ b/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedObject.java @@ -25,15 +25,15 @@ * * @author Christian Schwerdtfeger */ -class AnnotatedParameter { +class AnnotatedObject { final Annotation annotation; - final Object argument; + final Object object; - AnnotatedParameter(Annotation annotation, Object argument) { + AnnotatedObject(Annotation annotation, Object object) { this.annotation = annotation; - this.argument = argument; + this.object = object; } } diff --git a/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java b/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java index 2a49b4c172..18b507ff08 100644 --- a/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java +++ b/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java @@ -26,6 +26,7 @@ import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -89,9 +90,9 @@ public void addAnnotatedParameters(T objectToModify, ProceedingJoinPoint pjp) { try { Method method = ((MethodSignature) pjp.getSignature()).getMethod(); method = tryToTakeMethodFromTargetClass(pjp, method); - List annotatedParameters = AnnotationUtils.findAnnotatedParameters(annotationClass, - method, pjp.getArgs()); - getAnnotationsFromInterfaces(pjp, method, annotatedParameters); + List annotatedParameters = AnnotationUtils.findAnnotatedParameters(annotationClass, method, + pjp.getArgs()); + getParametersAnnotationsFromInterfaces(pjp, method, annotatedParameters); addAnnotatedArguments(objectToModify, annotatedParameters); } catch (Exception ex) { @@ -99,6 +100,26 @@ public void addAnnotatedParameters(T objectToModify, ProceedingJoinPoint pjp) { } } + public void addAnnotatedMethodResult(T objectToModify, ProceedingJoinPoint pjp, Object result) { + try { + Method method = ((MethodSignature) pjp.getSignature()).getMethod(); + method = tryToTakeMethodFromTargetClass(pjp, method); + + List annotatedResult = new ArrayList<>(); + Arrays.stream(method.getAnnotationsByType(annotationClass)) + .map(annotation -> new AnnotatedObject(annotation, result)) + .forEach(annotatedResult::add); + getMethodAnnotationsFromInterfaces(pjp, method).stream() + .map(annotation -> new AnnotatedObject(annotation, result)) + .forEach(annotatedResult::add); + + addAnnotatedArguments(objectToModify, annotatedResult); + } + catch (Exception ex) { + log.error("Exception occurred while trying to add annotated method result", ex); + } + } + private static Method tryToTakeMethodFromTargetClass(ProceedingJoinPoint pjp, Method method) { try { return pjp.getTarget().getClass().getDeclaredMethod(method.getName(), method.getParameterTypes()); @@ -109,34 +130,48 @@ private static Method tryToTakeMethodFromTargetClass(ProceedingJoinPoint pjp, Me return method; } - private void getAnnotationsFromInterfaces(ProceedingJoinPoint pjp, Method mostSpecificMethod, - List annotatedParameters) { + private void getParametersAnnotationsFromInterfaces(ProceedingJoinPoint pjp, Method mostSpecificMethod, + List annotatedParameters) { + traverseInterfacesHierarchy(pjp, mostSpecificMethod, method -> { + List annotatedParametersForActualMethod = AnnotationUtils + .findAnnotatedParameters(annotationClass, method, pjp.getArgs()); + // annotations for a single parameter can be `duplicated` by the ones + // from parent interface, + // however later on during key-based deduplication the ones from + // specific method(target class) + // will take precedence + annotatedParameters.addAll(annotatedParametersForActualMethod); + }); + } + + private void traverseInterfacesHierarchy(ProceedingJoinPoint pjp, Method mostSpecificMethod, + Consumer consumer) { Class[] implementedInterfaces = pjp.getThis().getClass().getInterfaces(); for (Class implementedInterface : implementedInterfaces) { for (Method methodFromInterface : implementedInterface.getMethods()) { if (methodsAreTheSame(mostSpecificMethod, methodFromInterface)) { - List annotatedParametersForActualMethod = AnnotationUtils - .findAnnotatedParameters(annotationClass, methodFromInterface, pjp.getArgs()); - // annotations for a single parameter can be `duplicated` by the ones - // from parent interface, - // however later on during key-based deduplication the ones from - // specific method(target class) - // will take precedence - annotatedParameters.addAll(annotatedParametersForActualMethod); + consumer.accept(methodFromInterface); } } } } + private List getMethodAnnotationsFromInterfaces(ProceedingJoinPoint pjp, Method mostSpecificMethod) { + List allAnnotations = new ArrayList<>(); + traverseInterfacesHierarchy(pjp, mostSpecificMethod, + method -> allAnnotations.addAll(Arrays.asList(method.getAnnotationsByType(annotationClass)))); + return allAnnotations; + } + private boolean methodsAreTheSame(Method mostSpecificMethod, Method method) { return method.getName().equals(mostSpecificMethod.getName()) && Arrays.equals(method.getParameterTypes(), mostSpecificMethod.getParameterTypes()); } - private void addAnnotatedArguments(T objectToModify, List toBeAdded) { + private void addAnnotatedArguments(T objectToModify, List toBeAdded) { Set seen = new HashSet<>(); - for (AnnotatedParameter container : toBeAdded) { - KeyValue keyValue = toKeyValue.apply(container.annotation, container.argument); + for (AnnotatedObject container : toBeAdded) { + KeyValue keyValue = toKeyValue.apply(container.annotation, container.object); if (seen.add(keyValue.getKey())) { keyValueConsumer.accept(keyValue, objectToModify); } diff --git a/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationUtils.java b/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationUtils.java index f2a056241b..4969365dc2 100644 --- a/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationUtils.java +++ b/micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationUtils.java @@ -35,14 +35,14 @@ private AnnotationUtils() { } - static List findAnnotatedParameters(Class annotationClazz, Method method, + static List findAnnotatedParameters(Class annotationClazz, Method method, Object[] args) { Parameter[] parameters = method.getParameters(); - List result = new ArrayList<>(); + List result = new ArrayList<>(); for (int i = 0; i < parameters.length; i++) { Parameter parameter = parameters[i]; for (Annotation annotation : parameter.getAnnotationsByType(annotationClazz)) { - result.add(new AnnotatedParameter(annotation, args[i])); + result.add(new AnnotatedObject(annotation, args[i])); } } return result; diff --git a/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java b/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java index fcddc4cc2b..04b20a1fe8 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java +++ b/micrometer-core/src/main/java/io/micrometer/core/aop/CountedAspect.java @@ -189,6 +189,7 @@ private Function> makeSafe( @Around("@within(io.micrometer.core.annotation.Counted) && !@annotation(io.micrometer.core.annotation.Counted) && execution(* *(..))") @Nullable + public Object countedClass(ProceedingJoinPoint pjp) throws Throwable { if (shouldSkip.test(pjp)) { return pjp.proceed(); @@ -241,17 +242,18 @@ private Object perform(ProceedingJoinPoint pjp, Counted counted) throws Throwabl Object result = pjp.proceed(); if (result == null) { if (!counted.recordFailuresOnly()) { - record(pjp, counted, DEFAULT_EXCEPTION_TAG_VALUE, RESULT_TAG_SUCCESS_VALUE); + record(pjp, result, counted, DEFAULT_EXCEPTION_TAG_VALUE, RESULT_TAG_SUCCESS_VALUE); } return result; } else { CompletionStage stage = ((CompletionStage) result); - return stage.whenComplete((res, throwable) -> recordCompletionResult(pjp, counted, throwable)); + return stage + .whenComplete((res, throwable) -> recordCompletionResult(pjp, result, counted, throwable)); } } catch (Throwable e) { - record(pjp, counted, e.getClass().getSimpleName(), RESULT_TAG_FAILURE_VALUE); + record(pjp, null, counted, e.getClass().getSimpleName(), RESULT_TAG_FAILURE_VALUE); throw e; } } @@ -259,30 +261,32 @@ private Object perform(ProceedingJoinPoint pjp, Counted counted) throws Throwabl try { Object result = pjp.proceed(); if (!counted.recordFailuresOnly()) { - record(pjp, counted, DEFAULT_EXCEPTION_TAG_VALUE, RESULT_TAG_SUCCESS_VALUE); + record(pjp, result, counted, DEFAULT_EXCEPTION_TAG_VALUE, RESULT_TAG_SUCCESS_VALUE); } return result; } catch (Throwable e) { - record(pjp, counted, e.getClass().getSimpleName(), RESULT_TAG_FAILURE_VALUE); + record(pjp, null, counted, e.getClass().getSimpleName(), RESULT_TAG_FAILURE_VALUE); throw e; } } - private void recordCompletionResult(ProceedingJoinPoint pjp, Counted counted, Throwable throwable) { + private void recordCompletionResult(ProceedingJoinPoint pjp, Object methodResult, Counted counted, + Throwable throwable) { if (throwable != null) { String exceptionTagValue = throwable.getCause() == null ? throwable.getClass().getSimpleName() : throwable.getCause().getClass().getSimpleName(); - record(pjp, counted, exceptionTagValue, RESULT_TAG_FAILURE_VALUE); + record(pjp, methodResult, counted, exceptionTagValue, RESULT_TAG_FAILURE_VALUE); } else if (!counted.recordFailuresOnly()) { - record(pjp, counted, DEFAULT_EXCEPTION_TAG_VALUE, RESULT_TAG_SUCCESS_VALUE); + record(pjp, methodResult, counted, DEFAULT_EXCEPTION_TAG_VALUE, RESULT_TAG_SUCCESS_VALUE); } } - private void record(ProceedingJoinPoint pjp, Counted counted, String exception, String result) { + private void record(ProceedingJoinPoint pjp, Object methodResult, Counted counted, String exception, + String result) { Counter.Builder builder = Counter.builder(counted.value()) .description(counted.description().isEmpty() ? null : counted.description()) .tags(counted.extraTags()) @@ -291,6 +295,7 @@ private void record(ProceedingJoinPoint pjp, Counted counted, String exception, .tags(tagsBasedOnJoinPoint.apply(pjp)); if (meterTagAnnotationHandler != null) { meterTagAnnotationHandler.addAnnotatedParameters(builder, pjp); + meterTagAnnotationHandler.addAnnotatedMethodResult(builder, pjp, methodResult); } builder.register(registry).increment(); } diff --git a/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTag.java b/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTag.java index 07415baf65..f6243224a7 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTag.java +++ b/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTag.java @@ -37,7 +37,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Inherited -@Target(ElementType.PARAMETER) +@Target({ ElementType.PARAMETER, ElementType.METHOD }) @Repeatable(MeterTags.class) public @interface MeterTag { diff --git a/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTags.java b/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTags.java index 28a954333d..64f8708961 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTags.java +++ b/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTags.java @@ -37,7 +37,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Inherited -@Target(ElementType.PARAMETER) +@Target({ ElementType.METHOD, ElementType.PARAMETER }) @Documented public @interface MeterTags { diff --git a/micrometer-core/src/main/java/io/micrometer/core/aop/TimedAspect.java b/micrometer-core/src/main/java/io/micrometer/core/aop/TimedAspect.java index fc2138d839..ffff8bf5d7 100644 --- a/micrometer-core/src/main/java/io/micrometer/core/aop/TimedAspect.java +++ b/micrometer-core/src/main/java/io/micrometer/core/aop/TimedAspect.java @@ -238,45 +238,47 @@ private Object processWithTimer(ProceedingJoinPoint pjp, Timed timed, String met try { Object result = pjp.proceed(); if (result == null) { - record(pjp, timed, metricName, sample, DEFAULT_EXCEPTION_TAG_VALUE); + record(pjp, result, timed, metricName, sample, DEFAULT_EXCEPTION_TAG_VALUE); return result; } else { CompletionStage stage = ((CompletionStage) result); - return stage.whenComplete( - (res, throwable) -> record(pjp, timed, metricName, sample, getExceptionTag(throwable))); + return stage.whenComplete((res, throwable) -> record(pjp, result, timed, metricName, sample, + getExceptionTag(throwable))); } } catch (Throwable e) { - record(pjp, timed, metricName, sample, e.getClass().getSimpleName()); + record(pjp, null, timed, metricName, sample, e.getClass().getSimpleName()); throw e; } } String exceptionClass = DEFAULT_EXCEPTION_TAG_VALUE; + Object result = null; try { - return pjp.proceed(); + result = pjp.proceed(); + return result; } catch (Throwable e) { exceptionClass = e.getClass().getSimpleName(); throw e; } finally { - record(pjp, timed, metricName, sample, exceptionClass); + record(pjp, result, timed, metricName, sample, exceptionClass); } } - private void record(ProceedingJoinPoint pjp, Timed timed, String metricName, Timer.Sample sample, - String exceptionClass) { + private void record(ProceedingJoinPoint pjp, Object methodResult, Timed timed, String metricName, + Timer.Sample sample, String exceptionClass) { try { - sample.stop(recordBuilder(pjp, timed, metricName, exceptionClass).register(registry)); + sample.stop(recordBuilder(pjp, methodResult, timed, metricName, exceptionClass).register(registry)); } catch (Exception e) { // ignoring on purpose } } - private Timer.Builder recordBuilder(ProceedingJoinPoint pjp, Timed timed, String metricName, + private Timer.Builder recordBuilder(ProceedingJoinPoint pjp, Object methodResult, Timed timed, String metricName, String exceptionClass) { Timer.Builder builder = Timer.builder(metricName) .description(timed.description().isEmpty() ? null : timed.description()) @@ -292,6 +294,7 @@ private Timer.Builder recordBuilder(ProceedingJoinPoint pjp, Timed timed, String if (meterTagAnnotationHandler != null) { meterTagAnnotationHandler.addAnnotatedParameters(builder, pjp); + meterTagAnnotationHandler.addAnnotatedMethodResult(builder, pjp, methodResult); } return builder; } diff --git a/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/CountedAspectTest.java b/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/CountedAspectTest.java index 0f2e004837..f5cc905c9e 100644 --- a/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/CountedAspectTest.java +++ b/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/CountedAspectTest.java @@ -27,6 +27,7 @@ import io.micrometer.core.instrument.search.MeterNotFoundException; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.aspectj.lang.ProceedingJoinPoint; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -471,17 +472,21 @@ class MeterTagsTests { CountedMeterTagAnnotationHandler meterTagAnnotationHandler = new CountedMeterTagAnnotationHandler( aClass -> valueResolver, aClass -> valueExpressionResolver); + MeterRegistry registry; + + CountedAspect countedAspect; + + @BeforeEach + void setup() { + registry = new SimpleMeterRegistry(); + countedAspect = new CountedAspect(registry); + countedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); + } + @ParameterizedTest @EnumSource void meterTagsWithText(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - CountedAspect countedAspect = new CountedAspect(registry); - countedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(countedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithCountedAspect(annotatedClass.newInstance()); service.getAnnotationForArgumentToString(15L); @@ -491,14 +496,7 @@ void meterTagsWithText(AnnotatedTestClass annotatedClass) { @ParameterizedTest @EnumSource void meterTagsWithResolver(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - CountedAspect countedAspect = new CountedAspect(registry); - countedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(countedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithCountedAspect(annotatedClass.newInstance()); service.getAnnotationForTagValueResolver("foo"); @@ -511,14 +509,7 @@ void meterTagsWithResolver(AnnotatedTestClass annotatedClass) { @ParameterizedTest @EnumSource void meterTagsWithExpression(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - CountedAspect countedAspect = new CountedAspect(registry); - countedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(countedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithCountedAspect(annotatedClass.newInstance()); service.getAnnotationForTagValueExpression("15L"); @@ -570,10 +561,6 @@ void multipleMeterTagsWithinContainerWithExpression(AnnotatedTestClass annotated @Test void meterTagOnPackagePrivateMethod() { - MeterRegistry registry = new SimpleMeterRegistry(); - CountedAspect countedAspect = new CountedAspect(registry); - countedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - AspectJProxyFactory pf = new AspectJProxyFactory(new MeterTagClass()); pf.setProxyTargetClass(true); pf.addAspect(countedAspect); @@ -585,6 +572,58 @@ void meterTagOnPackagePrivateMethod() { assertThat(registry.get("method.counted").tag("foo", "bar").counter().count()).isEqualTo(1); } + @ParameterizedTest + @EnumSource + void meterTagsOnReturnValueWithText(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithCountedAspect(annotatedClass.newInstance()); + + service.getAnnotationForArgumentToString(); + + assertThat(registry.get("method.counted").tag("test", "15").counter().count()).isEqualTo(1); + } + + @ParameterizedTest + @EnumSource + void meterTagsOnReturnValueWithResolver(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithCountedAspect(annotatedClass.newInstance()); + + service.getAnnotationForTagValueResolver(); + + assertThat(registry.get("method.counted") + .tag("test", "Value from myCustomTagValueResolver [foo]") + .counter() + .count()).isEqualTo(1); + } + + @ParameterizedTest + @EnumSource + void meterTagsOnReturnValueWithExpression(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithCountedAspect(annotatedClass.newInstance()); + + service.getAnnotationForTagValueExpression(); + + assertThat(registry.get("method.counted").tag("test", "hello characters").counter().count()).isEqualTo(1); + } + + @Test + void meterTagOnReturnValueOnPackagePrivateMethod() { + AspectJProxyFactory pf = new AspectJProxyFactory(new MeterTagClass()); + pf.setProxyTargetClass(true); + pf.addAspect(countedAspect); + + MeterTagClass service = pf.getProxy(); + + service.getAnnotationForPackagePrivateMethod(); + + assertThat(registry.get("method.counted").tag("foo", "bar").counter().count()).isEqualTo(1); + } + + private T getProxyWithCountedAspect(T object) { + AspectJProxyFactory pf = new AspectJProxyFactory(object); + pf.addAspect(countedAspect); + return pf.getProxy(); + } + enum AnnotatedTestClass { CLASS_WITHOUT_INTERFACE(MeterTagClass.class), CLASS_WITH_INTERFACE(MeterTagClassChild.class); @@ -612,13 +651,25 @@ interface MeterTagClassInterface { @Counted void getAnnotationForTagValueResolver(@MeterTag(key = "test", resolver = ValueResolver.class) String test); + @Counted + @MeterTag(key = "test", resolver = ValueResolver.class) + String getAnnotationForTagValueResolver(); + @Counted void getAnnotationForTagValueExpression( @MeterTag(key = "test", expression = "'hello' + ' characters'") String test); + @Counted + @MeterTag(key = "test", expression = "'hello' + ' characters'") + String getAnnotationForTagValueExpression(); + @Counted void getAnnotationForArgumentToString(@MeterTag("test") Long param); + @Counted + @MeterTag("test") + Long getAnnotationForArgumentToString(); + @Counted void getMultipleAnnotationsForTagValueExpression( @MeterTag(key = "value1", expression = "'value1: ' + value1") @MeterTag(key = "value2", @@ -640,21 +691,48 @@ public void getAnnotationForTagValueResolver( @MeterTag(key = "test", resolver = ValueResolver.class) String test) { } + @Counted + @MeterTag(key = "test", resolver = ValueResolver.class) + @Override + public String getAnnotationForTagValueResolver() { + return "foo"; + } + @Counted @Override public void getAnnotationForTagValueExpression( @MeterTag(key = "test", expression = "'hello' + ' characters'") String test) { } + @Counted + @MeterTag(key = "test", expression = "'hello' + ' characters'") + @Override + public String getAnnotationForTagValueExpression() { + return "15L"; + } + @Counted @Override public void getAnnotationForArgumentToString(@MeterTag("test") Long param) { } + @Counted + @MeterTag("test") + @Override + public Long getAnnotationForArgumentToString() { + return 15L; + } + @Counted void getAnnotationForPackagePrivateMethod(@MeterTag("foo") String foo) { } + @MeterTag("foo") + @Counted + String getAnnotationForPackagePrivateMethod() { + return "bar"; + } + @Counted @Override public void getMultipleAnnotationsForTagValueExpression( @@ -679,16 +757,34 @@ static class MeterTagClassChild implements MeterTagClassInterface { public void getAnnotationForTagValueResolver(String test) { } + @Counted + @Override + public String getAnnotationForTagValueResolver() { + return "foo"; + } + @Counted @Override public void getAnnotationForTagValueExpression(String test) { } + @Counted + @Override + public String getAnnotationForTagValueExpression() { + return "15L"; + } + @Counted @Override public void getAnnotationForArgumentToString(Long param) { } + @Counted + @Override + public Long getAnnotationForArgumentToString() { + return 15L; + } + @Counted @Override public void getMultipleAnnotationsForTagValueExpression( diff --git a/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/TimedAspectTest.java b/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/TimedAspectTest.java index 80b11cc955..f6e2cd56b5 100644 --- a/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/TimedAspectTest.java +++ b/samples/micrometer-samples-spring-framework6/src/test/java/io/micrometer/samples/spring6/aop/TimedAspectTest.java @@ -30,6 +30,7 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.micrometer.core.instrument.util.TimeUtils; import org.aspectj.lang.ProceedingJoinPoint; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -525,17 +526,21 @@ class MeterTagsTests { MeterTagAnnotationHandler meterTagAnnotationHandler = new MeterTagAnnotationHandler(aClass -> valueResolver, aClass -> valueExpressionResolver); + MeterRegistry registry; + + TimedAspect timedAspect; + + @BeforeEach + void setup() { + registry = new SimpleMeterRegistry(); + timedAspect = new TimedAspect(registry); + timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); + } + @ParameterizedTest @EnumSource void meterTagsWithText(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(timedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); service.getAnnotationForArgumentToString(15L); @@ -545,14 +550,7 @@ void meterTagsWithText(AnnotatedTestClass annotatedClass) { @ParameterizedTest @EnumSource void meterTagsWithResolver(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(timedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); service.getAnnotationForTagValueResolver("foo"); @@ -565,14 +563,7 @@ void meterTagsWithResolver(AnnotatedTestClass annotatedClass) { @ParameterizedTest @EnumSource void meterTagsWithExpression(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(timedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); service.getAnnotationForTagValueExpression("15L"); @@ -583,14 +574,7 @@ void meterTagsWithExpression(AnnotatedTestClass annotatedClass) { @ParameterizedTest @EnumSource void multipleMeterTagsWithExpression(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(timedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); service.getMultipleAnnotationsForTagValueExpression(new DataHolder("zxe", "qwe")); @@ -604,14 +588,7 @@ void multipleMeterTagsWithExpression(AnnotatedTestClass annotatedClass) { @ParameterizedTest @EnumSource void multipleMeterTagsWithinContainerWithExpression(AnnotatedTestClass annotatedClass) { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - - AspectJProxyFactory pf = new AspectJProxyFactory(annotatedClass.newInstance()); - pf.addAspect(timedAspect); - - MeterTagClassInterface service = pf.getProxy(); + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); service.getMultipleAnnotationsWithContainerForTagValueExpression(new DataHolder("zxe", "qwe")); @@ -625,10 +602,6 @@ void multipleMeterTagsWithinContainerWithExpression(AnnotatedTestClass annotated @Test void meterTagOnPackagePrivateMethod() { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); - AspectJProxyFactory pf = new AspectJProxyFactory(new MeterTagClass()); pf.setProxyTargetClass(true); pf.addAspect(timedAspect); @@ -642,21 +615,104 @@ void meterTagOnPackagePrivateMethod() { @Test void meterTagOnSuperClass() { - MeterRegistry registry = new SimpleMeterRegistry(); - TimedAspect timedAspect = new TimedAspect(registry); - timedAspect.setMeterTagAnnotationHandler(meterTagAnnotationHandler); + MeterTagSub service = getProxyWithTimedAspect(new MeterTagSub()); + + service.superMethod("someValue"); + + assertThat(registry.get("method.timed").tag("superTag", "someValue").timer().count()).isEqualTo(1); + } + + @ParameterizedTest + @EnumSource(AnnotatedTestClass.class) + void meterTagsOnReturnValueWithText(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); + + service.getAnnotationForArgumentToString(); + + assertThat(registry.get("method.timed").tag("test", "15").timer().count()).isEqualTo(1); + } + + @ParameterizedTest + @EnumSource(AnnotatedTestClass.class) + void meterTagsOnReturnValueWithResolver(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); + + service.getAnnotationForTagValueResolver(); + + assertThat(registry.get("method.timed") + .tag("test", "Value from myCustomTagValueResolver [foo]") + .timer() + .count()).isEqualTo(1); + } + + @ParameterizedTest + @EnumSource(AnnotatedTestClass.class) + void meterTagsOnReturnValueWithExpression(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); + + service.getAnnotationForTagValueExpression(); + + assertThat(registry.get("method.timed").tag("test", "hello characters. overridden").timer().count()) + .isEqualTo(1); + } + + @ParameterizedTest + @EnumSource(AnnotatedTestClass.class) + void multipleMeterTagsOnReturnValueWithExpression(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); + + service.getMultipleAnnotationsForTagValueExpression(); + + assertThat(registry.get("method.timed") + .tag("value1", "value1: zxe") + .tag("value2", "value2. overridden: qwe") + .timer() + .count()).isEqualTo(1); + } + + @ParameterizedTest + @EnumSource(AnnotatedTestClass.class) + void multipleMeterTagsOnReturnValueWithinContainerWithExpression(AnnotatedTestClass annotatedClass) { + MeterTagClassInterface service = getProxyWithTimedAspect(annotatedClass.newInstance()); + + service.getMultipleAnnotationsWithContainerForTagValueExpression(); + + assertThat(registry.get("method.timed") + .tag("value1", "value1: zxe") + .tag("value2", "value2: qwe") + .tag("value3", "value3. overridden: ZXEQWE") + .timer() + .count()).isEqualTo(1); + } - AspectJProxyFactory pf = new AspectJProxyFactory(new MeterTagSub()); + @Test + void meterTagOnReturnValueOnPackagePrivateMethod() { + AspectJProxyFactory pf = new AspectJProxyFactory(new MeterTagClass()); pf.setProxyTargetClass(true); pf.addAspect(timedAspect); - MeterTagSub service = pf.getProxy(); + MeterTagClass service = pf.getProxy(); - service.superMethod("someValue"); + service.getAnnotationForPackagePrivateMethod(); + + assertThat(registry.get("method.timed").tag("foo", "bar").timer().count()).isEqualTo(1); + } + + @Test + void meterTagOnReturnValueOnSuperClass() { + MeterTagSub service = getProxyWithTimedAspect(new MeterTagSub()); + + service.superMethod(); assertThat(registry.get("method.timed").tag("superTag", "someValue").timer().count()).isEqualTo(1); } + private T getProxyWithTimedAspect(T object) { + AspectJProxyFactory pf = new AspectJProxyFactory(object); + pf.addAspect(timedAspect); + return pf.getProxy(); + } + } enum AnnotatedTestClass { @@ -686,24 +742,47 @@ interface MeterTagClassInterface { @Timed void getAnnotationForTagValueResolver(@MeterTag(key = "test", resolver = ValueResolver.class) String test); + @Timed + @MeterTag(key = "test", resolver = ValueResolver.class) + String getAnnotationForTagValueResolver(); + @Timed void getAnnotationForTagValueExpression( @MeterTag(key = "test", expression = "'hello' + ' characters'") String test); + @Timed + @MeterTag(key = "test", expression = "'hello' + ' characters'") + String getAnnotationForTagValueExpression(); + @Timed void getAnnotationForArgumentToString(@MeterTag("test") Long param); + @Timed + @MeterTag("test") + Long getAnnotationForArgumentToString(); + @Timed void getMultipleAnnotationsForTagValueExpression( @MeterTag(key = "value1", expression = "'value1: ' + value1") @MeterTag(key = "value2", expression = "'value2: ' + value2") DataHolder param); + @Timed + @MeterTag(key = "value1", expression = "'value1: ' + value1") + @MeterTag(key = "value2", expression = "'value2: ' + value2") + DataHolder getMultipleAnnotationsForTagValueExpression(); + @Timed void getMultipleAnnotationsWithContainerForTagValueExpression(@MeterTags({ @MeterTag(key = "value1", expression = "'value1: ' + value1"), @MeterTag(key = "value2", expression = "'value2: ' + value2"), @MeterTag(key = "value3", expression = "'value3: ' + value1.toUpperCase + value2.toUpperCase") }) DataHolder param); + @Timed + @MeterTags({ @MeterTag(key = "value1", expression = "'value1: ' + value1"), + @MeterTag(key = "value2", expression = "'value2: ' + value2"), + @MeterTag(key = "value3", expression = "'value3: ' + value1.toUpperCase + value2.toUpperCase") }) + DataHolder getMultipleAnnotationsWithContainerForTagValueExpression(); + } static class MeterTagClass implements MeterTagClassInterface { @@ -714,21 +793,48 @@ public void getAnnotationForTagValueResolver( @MeterTag(key = "test", resolver = ValueResolver.class) String test) { } + @Timed + @MeterTag(key = "test", resolver = ValueResolver.class) + @Override + public String getAnnotationForTagValueResolver() { + return "foo"; + } + @Timed @Override public void getAnnotationForTagValueExpression( @MeterTag(key = "test", expression = "'hello' + ' characters.overridden'") String test) { } + @Timed + @MeterTag(key = "test", expression = "'hello' + ' characters. overridden'") + @Override + public String getAnnotationForTagValueExpression() { + return "foo"; + } + @Timed @Override public void getAnnotationForArgumentToString(@MeterTag("test") Long param) { } + @Timed + @MeterTag("test") + @Override + public Long getAnnotationForArgumentToString() { + return 15L; + } + @Timed void getAnnotationForPackagePrivateMethod(@MeterTag("foo") String foo) { } + @Timed + @MeterTag("foo") + String getAnnotationForPackagePrivateMethod() { + return "bar"; + } + @Timed @Override public void getMultipleAnnotationsForTagValueExpression( @@ -737,6 +843,14 @@ public void getMultipleAnnotationsForTagValueExpression( } + @Timed + @MeterTag(key = "value1", expression = "'value1: ' + value1") + @MeterTag(key = "value2", expression = "'value2. overridden: ' + value2") + @Override + public DataHolder getMultipleAnnotationsForTagValueExpression() { + return new DataHolder("zxe", "qwe"); + } + @Timed @Override public void getMultipleAnnotationsWithContainerForTagValueExpression(@MeterTags({ @@ -745,6 +859,16 @@ public void getMultipleAnnotationsWithContainerForTagValueExpression(@MeterTags( expression = "'value3.overridden: ' + value1.toUpperCase + value2.toUpperCase") }) DataHolder param) { } + @Timed + @MeterTags({ @MeterTag(key = "value1", expression = "'value1: ' + value1"), + @MeterTag(key = "value2", expression = "'value2: ' + value2"), + @MeterTag(key = "value3", + expression = "'value3. overridden: ' + value1.toUpperCase + value2.toUpperCase") }) + @Override + public DataHolder getMultipleAnnotationsWithContainerForTagValueExpression() { + return new DataHolder("zxe", "qwe"); + } + } static class MeterTagClassChild implements MeterTagClassInterface { @@ -754,29 +878,62 @@ static class MeterTagClassChild implements MeterTagClassInterface { public void getAnnotationForTagValueResolver(String test) { } + @Timed + @Override + public String getAnnotationForTagValueResolver() { + return "foo"; + } + @Timed @Override public void getAnnotationForTagValueExpression( @MeterTag(key = "test", expression = "'hello' + ' characters.overridden'") String test) { } + @Timed + @MeterTag(key = "test", expression = "'hello' + ' characters. overridden'") + @Override + public String getAnnotationForTagValueExpression() { + return "foo"; + } + @Timed @Override public void getAnnotationForArgumentToString(Long param) { } + @Timed + @Override + public Long getAnnotationForArgumentToString() { + return 15L; + } + @Timed @Override public void getMultipleAnnotationsForTagValueExpression( @MeterTag(key = "value2", expression = "'value2.overridden: ' + value2") DataHolder param) { } + @Timed + @Override + @MeterTag(key = "value2", expression = "'value2. overridden: ' + value2") + public DataHolder getMultipleAnnotationsForTagValueExpression() { + return new DataHolder("zxe", "qwe"); + } + @Timed @Override public void getMultipleAnnotationsWithContainerForTagValueExpression(@MeterTag(key = "value3", expression = "'value3.overridden: ' + value1.toUpperCase + value2.toUpperCase") DataHolder param) { } + @Timed + @MeterTag(key = "value3", expression = "'value3. overridden: ' + value1.toUpperCase + value2.toUpperCase") + @Override + public DataHolder getMultipleAnnotationsWithContainerForTagValueExpression() { + return new DataHolder("zxe", "qwe"); + } + } static class MeterTagSuper { @@ -785,6 +942,12 @@ static class MeterTagSuper { public void superMethod(@MeterTag("superTag") String foo) { } + @Timed + @MeterTag("superTag") + public String superMethod() { + return "someValue"; + } + } static class MeterTagSub extends MeterTagSuper { @@ -793,6 +956,12 @@ static class MeterTagSub extends MeterTagSuper { public void subMethod(@MeterTag("subTag") String foo) { } + @Timed + @MeterTag("subTag") + public String subMethod() { + return "someValue"; + } + } private static final class FailingMeterRegistry extends SimpleMeterRegistry {