0

Existing answers gives nice explanation on how to use Custom Annotation for method execution time logging. I am wondering if there is way to use same annotation for both Class and Method, but Pointcut should be different where it is used.

@LogExecutionTime
public class MyServiceImpl implements MyService {

    public void run(){
       // logic
    }

    public void walk(){
       // logic
    }

    private void breather(){
       // logic
    }
}

If Annotation is used for class all methods inside class should be considered for Execution Time Logging in Aspect Class (like execution(* com.me.package.MyServiceImpl.*(..))). However if the Annotation is only used for single method inside the class, it is should also consider that only method in Aspect Logging Class. (like execution(* com.you.package.YourServiceImpl.forward(..))).

public class YourServiceImpl implements YourService {

    @LogExecutionTime
    public void forward(){
       // logic
    }

    @LogExecutionTime
    public void backward(){
       // logic
    }

    private void upward(){
       // logic
    }
}

Annotation Class

package com.myproj.core.utils.annotation;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;

@Retention(RUNTIME)
public @interface LogExecutionTime {

}

Aspect Class for Annotation (using pointcuts as suggested by @kriegaex)

package com.myproj.core.utils;

import java.time.Duration;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Aspect class to Log Method Execution time
 */
@Aspect
public class MyMethodExecutionLoggingAspect {
    
    private static final Logger LOG = LoggerFactory.getLogger(MyMethodExecutionLoggingAspect.class);
    
    /**
     * This method will log Method Execution Time
     * 
     * @param joinPoint
     * @return object
     * @throws Throwable
     */
    @Around("execution(* (@com.myproj.core.utils.annotation.LogExecutionTime *).*(..)) || execution(@com.myproj.core.utils.annotation.LogExecutionTime * *(..))")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {

        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        Instant start = Instant.now();

        try {
            return joinPoint.proceed();
        } finally {

            long end = Duration.between(start, Instant.now()).toMillis();

            if (end > 0) {
                LOG.debug("METHOD EXECUTION TIME LAPSED: {}ms | {}.{}", end, className, methodName);
            }
        }

    }
    
}

Spring Bean Definition in spring.xml

<bean class="com.myproj.core.utils.MyMethodExecutionLoggingAspect" />

Zedex7
  • 1,624
  • 1
  • 12
  • 21
  • Have you given up on this? No more feedback? It is still on my follow-up list. – kriegaex Oct 19 '20 at 01:33
  • @kriegaex sorry as replied earlier it is not working as expected. I tried to use only class level but somehow it is only logging public methods only. So for now, I am using another approach of directly giving qualified names of methods in ```@Around```. I am definitely going to implement this, but for now it is on hold. Sorry for the trouble. – Zedex7 Oct 20 '20 at 08:55
  • I am convinced that my answer is correct. The only reason I can think of that it does not work for you is an error in your pointcut. Because the latter is exactly what you hide in your sample code, the question is closed for now. I am sure it will be reopened as soon as you update the code with a a concrete pointcut. I would be happy to further assist if I had something to analyse. – kriegaex Oct 21 '20 at 00:01
  • Thanks @kriegaex, even I am sure those pointcuts are correct, but somehow they are not working as expected. FYI I have added full aspect class along with pointcuts as you have provided. – Zedex7 Oct 21 '20 at 15:29
  • Just to make sure you did not forget anything fundamental: If you use Spring AOP, both the aspect and the target classes must be `@Component`s or declared as `@Bean`s (or more generally, must be Spring-managed) in order to be picked up by Spring AOP. I cannot see anything of the sort in your code. So what does the original code look like? – kriegaex Oct 21 '20 at 15:40
  • Yes, beans are defined in spring.xml file. Aspect class getting picked by AOP as only Class Level annotation is giving result by logging public methods. – Zedex7 Oct 21 '20 at 15:50
  • Could you please condense your problem into an [MCVE](https://stackoverflow.com/help/mcve) and push it to GitHub? At this point I think I need to see for myself, complete with code, configuration and Maven build (or Gradle, if must be). – kriegaex Oct 21 '20 at 16:09
  • I afraid that can't be done, code is part of Customization for SAP Commerce Cloud. Although I can create standalone Spring Boot Project and try there, that way we'll understand Its pointcut issue or SAP CC is creating issue. I'll keep you posted. – Zedex7 Oct 22 '20 at 05:10
  • Yes, that is what MCVE means, if you read the article: not the full original application but a minimal, self-consistent sample reproducing the problem. Besides, 90% of the time developers learn so much about the problem by isolating it and removing stuff unrelated to it, that they are much closer to the root cause or even are able to find and resolve it. – kriegaex Oct 22 '20 at 08:03

1 Answers1

1

Your sample code with @annotation() does not make sense because you do not specify a method annotation type. It should be something like @annotation(fully.qualified.AnnotationType) instead.

For matching class annotations you want to use @within(fully.qualified.AnnotationType), as described here.

So you could use a pointcut like

@annotation(fully.qualified.AnnotationType)
  || @within(fully.qualified.AnnotationType)

Alternatively, according to my answer here, you could also use the more cryptic version

execution(* (@fully.qualified.AnnotationType *).*(..))
  || execution(@fully.qualified.AnnotationType * *(..))
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks, Pointcut was the missing piece I was looking for. I followed your both approach, but only Class level annotation is triggering aspect, method level annotation is somehow not working. – Zedex7 Oct 08 '20 at 13:28
  • Then you did something different from what I told you. You didn't even mention which of the two approaches you chose. Besides, "it is not working" is not a particularly helpful problem description. – kriegaex Oct 08 '20 at 15:57
  • I tried both approach ```@annotation(..``` and ```execution```, but it seems these pointcuts are only working for class level annotation and not for method level annotation. I have found another answer https://stackoverflow.com/questions/36175368/pointcut-for-annotated-methods-or-methods-in-annotated-classes?rq=1 , but it is also giving same result. – Zedex7 Oct 08 '20 at 16:36
  • Do you think it is a particularly smart idea to update your question by pseudo code `@Around("")`? Exactly the part you are having problems with is what you are hiding. If you seriously want help, please reconsider your way of asking questions and show exactly and reproducibly what you did. People around here are developers, i.e. they want to analyse and solve problems, not guess or speculate. – kriegaex Oct 09 '20 at 00:24
  • @kriegaex : actually this is what I thought would work, but does not work for me either. I want to have a Pointcut at either the annotation (which is easy possible using `@annotation(com.package.SomeCustomAnno)`) as well as other possible annotations, which could in turn be annotated with `@SomeCustomAnno(value = ..., bar = ...)`. Sadly I cannot get the "derived" annotation to work (Here's the question: https://stackoverflow.com/q/68785567/15610035). If you have an idea what to do in order to react to that derived annotation, please let me know, but the solution offered here did not work. – Voronin Aug 14 '21 at 23:15
  • Please do not hijack other questions. I would have noticed and answered your new question due to the correct tags already. My answer here is correct, but you made a syntax error transferring it to your own situation, see my upcoming answer to the new question. – kriegaex Aug 15 '21 at 01:49