I am working with a little project intended to allow applications to benefit from aspect oriented programming. The first aspect is logging, which is already working. However I want this code to be more performatic and cleaner.
The basic goals is to annotate methods - private/public static or instance - with and Custom annotation @LoggableObjects
. This annotation has options to define level of log - how much information to log (only args values, profiling information).
Since it uses reflection in many steps, I am afraid I might have missed something. I would appreciate if anyone reviewed my code.
This code processes the annotations and instruments the methods. How can the method aroundObjects
be better executed? Is this a valid idea?
package org.greentea.aspect.log; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.greentea.aspect.log.annotation.LoggableObject; /** * LoggableObjects are either Methods or Constructors that migh be logged <br> * The information logged is the method signature, its arguments and execution * time * * The level of verbosity, and information might be configurable through the use * of the annotation @LoggableObjet * * @author Filipe Gonzaga Miranda */ @Aspect public class LoggableObjects { PluggableLogger pluggableLogger; public static ConcurrentMap<Class<? extends PluggableLogger>, PluggableLogger> cachedLoggers = new ConcurrentHashMap<>(); static{ cachedLoggers.putIfAbsent(DefaultPluggableLoggerIfNotInjected.class, new DefaultPluggableLoggerIfNotInjected()); } /** * Captures the Annotations {@link LoggableObjects} * * And applies the logic to decide how to log the information based on the * LogModes * * @param proJoinPoint * @return * @throws Throwable */ @Around("execution(* *(..)) && @annotation(LoggableObject)") public Object aroundObjects(ProceedingJoinPoint proJoinPoint) throws Throwable { Signature methodSignature = proJoinPoint.getSignature(); String declaringClass = methodSignature.getDeclaringTypeName(); String methodName = methodSignature.getName(); Object[] args = proJoinPoint.getArgs(); LoggableObject loggObject = getLoggableObjectAnnt(args, methodSignature); if (loggObject.disable()) { return proJoinPoint.proceed(); } Class<? extends PluggableLogger> clazzPluggLogg = loggObject.pluggableLoggerClass(); if(clazzPluggLogg != DefaultPluggableLoggerIfNotInjected.class){ if(cachedLoggers.containsKey(clazzPluggLogg)){ pluggableLogger = cachedLoggers.get(clazzPluggLogg); }else{ pluggableLogger = clazzPluggLogg.newInstance(); cachedLoggers.putIfAbsent(clazzPluggLogg, pluggableLogger); } }else{ pluggableLogger = cachedLoggers.get(clazzPluggLogg); } LoggableObject.LogModes[] logModes = loggObject.logMode(); boolean profileMode = false; boolean argsMode = false; boolean methodMode = false; logModesLoop: for (int i = 0; i < logModes.length; i++) { switch (logModes[i]) { case ALL: profileMode = true; argsMode = true; methodMode = true; break logModesLoop; case PROFILE: profileMode = true; break; case METHOD_NAME: methodMode = true; break; case ARGS: argsMode = true; break; default: throw new AssertionError("Operation not supported. " + logModes[i].name()); } } PluggableLogger logger = pluggableLogger; // getting modifiers if (methodMode || argsMode) { logger.logInfo(String.format( "Entering execution of method %s, of class %s", methodName, declaringClass)); } if (argsMode && args.length > 0) { logger.logInfo(String.format("Arguments of method %s are: %s", methodName, java.util.Arrays.toString(args))); } Object returnObject; try { long startNanoTime = System.nanoTime(); returnObject = proJoinPoint.proceed(); long execTime = System.nanoTime() - startNanoTime; if (profileMode) { logger.logInfo(String.format( "Finished execution of %s in %s nanoTime", methodName, execTime)); } } catch (Exception e) { logger.logError("\nException in the method " + methodName + " of class: " + methodSignature.getDeclaringTypeName()); if (!loggObject.exceptionOnlyToExceptions()) { throw e; } logger.logWarning("Exception " + e + " supressed. Setting returning value to null..."); returnObject = null; } return returnObject; } @SuppressWarnings("unchecked") private LoggableObject getLoggableObjectAnnt(Object[] args, Signature methodSignature) throws Exception{ int loadedClasses = 0; Class<?> clazzTypes[] = new Class[args.length]; for (int i = 0; i < args.length; i++) { Class<?> clazz = null; if (args[i] == null) { String completeSignature = methodSignature.toLongString(); Pattern p = Pattern .compile("[\\w+.-]+([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*"); Matcher m = p.matcher(completeSignature); m.find(); m.find(); m.find(); int pos = 0; while(pos < loadedClasses){ m.find(); pos++; } m.find(); String clazzName = m.group(); clazz = Class.forName(clazzName); loadedClasses++; }else{ clazz = args[i].getClass(); loadedClasses++; } clazzTypes[i] = clazz; } return methodSignature.getDeclaringType() .getDeclaredMethod(methodSignature.getName(), clazzTypes) .getAnnotation(LoggableObject.class); } /** * Default implementation of {@link PluggableLogger} using * {@link java.util.logging.Logger} internally to Log msgs * @author Filipe Gonzaga Miranda */ public static class DefaultPluggableLoggerIfNotInjected implements PluggableLogger{ @Override public void logInfo(String msg) { Logger logger = Logger.getLogger("greentea.logger"); logger.log(Level.INFO, msg); } @Override public void logError(String msg) { Logger logger = Logger.getLogger("greentea.logger"); logger.log(Level.SEVERE, msg); } @Override public void logWarning(String msg) { Logger logger = Logger.getLogger("greentea.logger"); logger.log(Level.WARNING, msg); } @Override public PluggableLogger getForCache() { return this; } @Override public String toString() { return "DefaultPluggableLoggerIfNotInjected - It has a java.util.logging.Logger"; } } }