How to log Spring Data JPA repository method execution time? - java

I have simple Spring Data JPA repository.
public interface UserRepository extends JpaRepository<UserEntity, Serializable>{ … }
Is there any way to monitor execution time for methods generated by Spring (for example findOne(…))?

The easiest way is to use a CustomizableTraceInterceptor as follows:
#Configuration
#EnableAspectJAutoProxy
public class SpringDataExecutionLoggingConfiguration {
#Bean
public CustomizableTraceInterceptor customizableTraceInterceptor() {
CustomizableTraceInterceptor customizableTraceInterceptor = new CustomizableTraceInterceptor();
customizableTraceInterceptor.setUseDynamicLogger(true);
customizableTraceInterceptor.setExitMessage("Executed $[methodName] in $[invocationTime]");
return customizableTraceInterceptor;
}
#Bean
public Advisor advisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(public * org.springframework.data.repository.CrudRepository+.*(..))");
return new DefaultPointcutAdvisor(pointcut, customizableTraceInterceptor());
}
}

Another working solution:
#Aspect
#Component
public class ProfilerAspect {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Pointcut("execution(public * org.springframework.data.repository.Repository+.*(..))")
public void monitor() {}
#Around("monitor()")
public Object profile(ProceedingJoinPoint pjp) {
long start = System.currentTimeMillis();
logger.debug("JVM memory in use = "+ (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
Object output = null;
try {
output = pjp.proceed();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
long elapsedTime = System.currentTimeMillis() - start;
logger.debug(pjp.getTarget()+"."+pjp.getSignature()+": Execution time: " + elapsedTime + " ms. ("+ elapsedTime/60000 + " minutes)");
return output;
}
}

Related

Spring aop advice failed to wrapped proxy when the advice bean is in creation

The issue is the same as this question
I am working with the spring aop at annotation aspect, one of my service bean cannot install annotation advice but others can.
Then I debug and found out the reason, the code in BeanFactoryAdvisorRetrievalHelper indicates that if an advice bean is in creation, the serviceBean will failed to install the advisor.
if (this.beanFactory.isCurrentlyInCreation(name))
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
The interceptor is
public class DemoInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//do something
return new Object();
}
}
The advisor config is
config way 1
public class DemoAdvisor implements PointcutAdvisor {
private MethodInterceptor methodInterceptor;
private Pointcut pointcut;
public DemoAdvisor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
this.pointcut = new AnnotationMatchingPointcut(null, DemoAnno.class);
}
#Override
public Advice getAdvice() {
return this.methodInterceptor;
}
#Override
public boolean isPerInstance() {
return true;
}
#Override
public Pointcut getPointcut() {
return this.pointcut;
}
}
#Configuration
public class AnnoAspectConfig {
#Bean
public DemoAdvisor DemoAdvisor() {
DemoAdvisor advisor = new DemoAdvisor(demoInterceptor());
return advisor;
}
#Bean
public DemoInterceptor demoInterceptor() {
DemoInterceptor demoInterceptor = new DemoInterceptor();
return demoInterceptor;
}
}
Then I think it maybe the problem in the advice config, so I change the advice config to the below
config way 2
#Configuration
public class ValidationAdvisor {
#Bean
public DefaultPointcutAdvisor demoAdvisor() {
DemoInterceptor interceptor = new DemoInterceptor();
AnnotationMatchingPointcut annotationMatchingPointcut = AnnotationMatchingPointcut.forMethodAnnotation(DemoAnno.class);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(annotationMatchingPointcut);
advisor.setAdvice(interceptor);
return advisor;
}
}
Then everything goes find, so the question is, why the config way 1 cause the failed of advisor installation while the config way 2 did not?

Spring AOP does not work with a small functions

I have a typical task to check proceed time of many functions. I made an annotation
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface ProceedTimeLogger {}
and an aspect for this annotation
#Aspect
#Component
public class ProceedTimeLoggerAspect {
public static final Logger LOGGER = LoggerFactory.getLogger(ProceedTimeLoggerAspect.class);
#Around(value = "(#within(com.security.annotation.ProceedTimeLogger) " +
"|| #annotation(com.security.annotation.ProceedTimeLogger))" +
"|| execution(* com.security.permissions.permissions.*+.*(..))")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
String methodId = getMethodId();
long start = System.currentTimeMillis();
LOGGER.info("method '{}' id={} started", methodName, methodId);
Object proceed = pjp.proceed();
long end = System.currentTimeMillis() - start;
LOGGER.info("method '{}' id={} proceed time = {} ms", methodName, methodId, end);
return proceed;
}
public String getMethodId() {
String randomUUID = UUID.randomUUID().toString();
return randomUUID.substring(randomUUID.lastIndexOf("-") + 1, randomUUID.length());
}
}
Now my annotation covers som about 40 classes, but from all of this classes it works only in one method:
#Component
#ProceedTimeLogger
public class Permission {
...some methods...
//this function covers!!
public PermissionDTO getDTO(String permissionVal, String productDictId) {
PermissionDTO result = new PermissionDTO();
result.setMenuView(isMenuView(productDictId));
result.setDataView(isDataView(productDictId));
result = initOptionalDtoFields(result, productDictId);
return result;
}
...another non-covered methods...
public String getProductType(String productDictId) {
if (productDictId == null) {
return null;
}
Product product = dataDispatcher.getDictionaryById(Product.class, Long.valueOf(productDictId));
return Optional.ofNullable(product.getType())
.map(BaseDict::getIdMDMP)
.map(String::valueOf)
.orElse(null);
}
#ProceedTimeLogger
public String getProductCategory(String productDictId) {
if (productDictId == null) {
return null;
}
Product product = dataDispatcher.getDictionaryById(Product.class, Long.valueOf(productDictId));
return Optional.ofNullable(product.getProductCategory())
.map(BaseDict::getIdMDMP)
.map(String::valueOf)
.orElse(null);
}
}
Wtat's different? Why does these methods are not being covered?

Spring Boot AOP

I am having some issues trying to get my advice to execute. I tried several different pointcuts to no avail. The "#EnableAspectJProxy" seems to be working and detects my aspect. Any advice is appreciated.
I am using spring-boot-aop-starter.
#Aspect
#Component
public class ExecutionTimeLogger {
private Logger logger;
public ExecutionTimeLogger() {
logger = LoggerFactory.getLogger(getClass());
logger.info("HEY");
}
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {}
#Pointcut("execution(* edu.x.y.z.server.web.controller.*.*(*))")
public void methodPointcut() {}
#Pointcut("within(#org.springframework.web.bind.annotation.RequestMapping *)")
public void requestMapping() {}
#Around("controller() && methodPointcut() && requestMapping()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch();
String name = pjp.getSignature().getName();
try {
sw.start();
return pjp.proceed();
} finally {
sw.stop();
logger.info("STOPWATCH: " + sw.getTime() + " - " + name);
}
}
}
I am trying to match any method that is within my package and is annotated with the #RequestMapping annotation. I have tried the very generic match any and all methods without any luck too.
Here is a sample of a method that the advice should be applied to:
#RequestMapping(value = "/analysis", method = RequestMethod.GET)
#ApiOperation(value = "Get analyses available for the current user")
JsonModelAndView getAllAnalyses(HttpServletRequest request)
I managed to get this resolved. I ended up creating a small spring application to test the use case with the specific pointcuts to remove other potential barriers. I found that my pointcuts needed some adjusting.
#Aspect
#Component
public class ExecutionTimeLogger {
private Logger logger;
public ExecutionTimeLogger() {
logger = LoggerFactory.getLogger(getClass());
logger.info("HEY");
}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
#Pointcut("execution(* edu.x.y.z.server.web.controller.*Controller.*(..))")
public void methodPointcut() {}
#Around("requestMapping() && methodPointcut()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch();
String name = pjp.getSignature().getName();
try {
sw.start();
return pjp.proceed();
} finally {
sw.stop();
logger.info("STOPWATCH: " + sw.getTime() + " - " + name);
}
}
}
As you can see the big difference was the annotation pointcut.
You might want to set proxyTargetClass=true (assuming your controllers do not have an interface). Use your own #EnableASpectJAutoProxy or set spring.aop.proxyTargetClass=true.

Java - Execute a class method with a specify annotation

I have a android application, but it is not relevant.
I have a class called "Front controller" which will receive some message
through it's constructor. The message, for brievity, could be an integer.
I want somewhere else to create a new controller which will execute
a method based on the integer defined above
public class OtherController {
#MessageId("100")
public void doSomething(){
//execute this code
}
#MessageId("101")
public void doSomethingElse(){
//code
}
}
The front controller could be something like this:
public class FrontController {
private int id;
public FrontController(int id){
this.id=id;
executeProperControllerMethodBasedOnId();
}
public void executeProperControllerMethodBasedOnId(){
//code here
}
public int getId(){
return id;
}
}
So, if the Front Controller will receive the integer 100, it
will execute the method annotated with #MessageId(100). The
front controller don't know exactly the class where this method
is.
The problem which I found is that I need to register somehow
each controller class. I Spring I had #Component or #Controller
for autoloading. After each controllers are register, I need to
call the properly annotated method.
How to achieve this task? In Spring MVC, I had this system
implemented, used to match the HTTP routes. How could I implement
this in a plain java project?
Any suggestions?
Thanks to Google Reflections (hope you can integrate this in your android project.)
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections-maven</artifactId>
<version>0.9.8</version>
</dependency>
For optimisation I've added the requirement to also annotate the class with MessageType annotation and the classes should be in the same package (org.conffusion in my example):
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MessageType {
}
The OtherController looks like:
#MessageType
public class OtherController {
#MessageId(id=101)
public void method1()
{
System.out.println("executing method1");
}
#MessageId(id=102)
public void method2()
{
System.out.println("executing method2");
}
}
The implementation will look like:
public void executeProperControllerMethodBasedOnId() {
Set<Class<?>> classes = new org.reflections.Reflections("org.conffusion")
.getTypesAnnotatedWith(MessageType.class);
System.out.println("found classes " + classes.size());
for (Class<?> c : classes) {
for (Method m : c.getMethods()) {
try {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object o = c.newInstance();
if (mid.id() == id)
m.invoke(o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Maybe you can optimise and build a static hashmap containing already scanned message ids.
You need to implement some of the work by yourself using reflection, I would recommend to prepare message handlers on initial phase in regards to performance. Also you possibly want to think about Singleton/Per Request controllers. Some of the ways to implement the solution:
interface MessageProcessor {
void execute() throws Exception;
}
/* Holds single instance and method to invoke */
class SingletonProcessor implements MessageProcessor {
private final Object instance;
private final Method method;
SingletonProcessor(Object instance, Method method) {
this.instance = instance;
this.method = method;
}
public void execute() throws Exception {
method.invoke(instance);
}
}
/* Create instance and invoke the method on execute */
class PerRequestProcessor implements MessageProcessor {
private final Class clazz;
private final Method method;
PerRequestProcessor(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
public void execute() throws Exception {
Object instance = clazz.newInstance();
method.invoke(instance);
}
}
/* Dummy controllers */
class PerRequestController {
#MessageId(1)
public void handleMessage1(){System.out.println(this + " - Message1");}
}
class SingletonController {
#MessageId(2)
public void handleMessage2(){System.out.println(this + " - Message2");}
}
class FrontController {
private static final Map<Integer, MessageProcessor> processors = new HashMap<Integer, MessageProcessor>();
static {
try {
// register your controllers
// also you can scan for annotated controllers as suggested by Conffusion
registerPerRequestController(PerRequestController.class);
registerSingletonController(SingletonController.class);
} catch (Exception e) {
throw new ExceptionInInitializerError();
}
}
private static void registerPerRequestController(Class aClass) {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
processors.put(mid.value(), new PerRequestProcessor(aClass, m));
}
}
}
private static void registerSingletonController(Class aClass) throws Exception {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object instance = aClass.newInstance();
processors.put(mid.value(), new SingletonProcessor(instance, m));
}
}
}
/* To process the message you just need to look up processor and execute */
public void processMessage(int id) throws Exception {
if (processors.containsKey(id)) {
processors.get(id).execute();
} else {
System.err.print("Processor not found for message " + id);
}
}
}

Spring AOP Aspect not executing

I've been trying to figure out why my simple aspect is not getting executed. I looked at the answers of similar problems but i still can't get it to work.
My intention is to wrap the execution of a method annotated with a custom annotation with an AOP advice that will track how long the method takes to run. When i run my test, i see the output of the method but the advice is not being run (i'm expecting it to log some output).
Here's the Aspect class:
#Aspect
class LatencyProfiler {
private LatencyTrackerFactory factory = LatencyTrackerFactory.NOOP;
#Pointcut(value="execution(#ProfileLatency * *(..)) && #annotation(annotation)", argNames="annotation")
public void profiled(ProfileLatency annotation) {}
#Around(value="profiled(annotation)", argNames="pjp,annotation")
public Object profile(ProceedingJoinPoint pjp, ProfileLatency annotation) throws Throwable {
ILatencyTracker tracker;
try {
tracker = factory.create(annotation.trackerName(), annotation.trackerNameSuffix());
} catch (ConfigException e) {
throw new RuntimeException(e);
}
tracker.begin();
Object ret = pjp.proceed();
tracker.end(null);
return ret;
}
#Optional
public void setFactory(LatencyTrackerFactory factory) {
this.factory = factory;
}
}
Followed by the Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface ProfileLatency {
String trackerName();
String trackerNameSuffix() default"[unassigned]";
}
Followed by a test class:
public class Test {
private static final Log LOG = LogFactory.getLog(Test.class);
#PostConstruct
public void init() {
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
#Override
public void run() {
for(int i = 0; i < 60; i++) {
foo();
LOG.info("HERE");
}
}
}, 2000, TimeUnit.MILLISECONDS);
}
#ProfileLatency(trackerName = "latency", trackerNameSuffix = "s")
public void foo() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
}
Spring configuration:
<context:annotation-config/>
<aop:aspectj-autoproxy>
<aop:include name="latencyProfileAspect"/>
</aop:aspectj-autoproxy>
<bean
id = "latencyLogger"
class = "util.logging.LatencyLogger"
/>
<bean
id = "trackerFactory"
class = "util.latency.LatencyTrackerFactoryImpl">
<constructor-arg value = "config/latency-config.xml"/>
<constructor-arg ref = "latencyLogger"/>
</bean>
<bean
id = "latencyProfileAspect"
class = "util.latency.aop.LatencyProfiler"
p:factory-ref = "trackerFactory"
/>
<bean id = "test" class="util.Test"/>
and finally the test's output:
21:20:37,930 INFO main/SpringMain - Ready.
21:20:40,928 INFO pool-4-thread-1/Test - HERE
21:20:41,927 INFO pool-4-thread-1/Test - HERE
21:20:42,926 INFO pool-4-thread-1/Test - HERE
21:20:43,925 INFO pool-4-thread-1/Test - HERE
21:20:44,924 INFO pool-4-thread-1/Test - HERE
...
Any advice is greatly appreciated.
So i fiddled with this around a bit and got it to work. I modified the aspect as follows:
#Aspect
public class LatencyProfiler {
private static final Log LOG = LogFactory.getLog(LatencyProfiler.class);
#Around("#annotation(annotation)")
public Object profile(ProceedingJoinPoint pjp, ProfileLatency annotation) throws Throwable {
ILatencyTracker tracker = ILatencyTracker.NOOP;
try {
tracker = StaticLatencyTrackerFactory.getTracker(annotation.trackerName(), annotation.trackerNameSuffix());
} catch (Exception e) {
LOG.error(e);
}
LatencyContext ctx = tracker.beginContext();
Object ret = pjp.proceed();
ctx.end();
return ret;
}
/*
* special purpose factory method
*/
public static LatencyProfiler aspectOf() {
return MyAspectHolder.instance;
}
/**
* private class holding the singleton
*/
private static class MyAspectHolder {
static final LatencyProfiler instance = new LatencyProfiler();
}
}
i also changed the spring configuration to be:
<context:annotation-config/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean
id = "latencyProfileAspect"
class = "util.latency.aop.LatencyProfiler"
factory-method = "aspectOf"
/>
<bean id = "test" class="util.Test"/>

Categories