package de.flower.common.aop; import org.apache.commons.collections.ArrayStack; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Modifier; /** * @author flowerrrr * @NotThreadSafe Uses unsynchronized and not thread context bound stack to * trace start time in #logEnter. */ // @NotThreadSafe @Aspect public abstract class AbstractSlf4JLoggingAspect extends AbstractLoggingAspect { private int depth = 1; private ArrayStack stack = new ArrayStack(); @Override protected void logEnter(JoinPoint jp, boolean indent) { Logger log = getLogger(jp.getStaticPart()); // public method -> debug, private -> trace boolean privateMethod = isPrivateMethod(jp.getStaticPart()); boolean logEnabled = (!privateMethod) ? log.isDebugEnabled() : log.isTraceEnabled(); if (logEnabled) { String msg; msg = (indent == true) ? indent(depth++, ">>") : indent(depth, ">>") + " new "; // msg += "<" + jp.getKind(); msg += " " + jp.getSignature().toShortString(); StringBuffer args = new StringBuffer(); jp.getSignature().toShortString(); for (Object arg : jp.getArgs()) { if (args.length() != 0) { args.append(", "); } if (arg != null) { args.append(arg.getClass().getSimpleName() + ": [" + arg.toString() + "]"); } else { args.append("Object: null"); } } // push start of execution time onto stack if (indent) { pushTime(); } final String argss = args.toString(); msg += (StringUtils.isEmpty(argss)) ? "" : "(" + argss + ")"; if (privateMethod == false && indent /* no debug for constructors */) { log.debug(msg); } else { log.trace(msg); } } } private boolean isPrivateMethod(JoinPoint.StaticPart jp) { return Modifier.isPrivate(jp.getSignature().getModifiers()); } @Override protected void logExit(JoinPoint.StaticPart jp) { Logger log = getLogger(jp); boolean privateMethod = isPrivateMethod(jp); boolean logEnabled = (!privateMethod) ? log.isDebugEnabled() : log.isTraceEnabled(); if (logEnabled) { String msg; msg = indent(--depth, "<<"); long time = System.currentTimeMillis() - popTime(); msg += " [" + time + " ms]"; msg += " " + jp.getSignature().toShortString(); if (privateMethod == false) { log.debug(msg); } else { log.trace(msg); } } } private void pushTime() { stack.push(System.currentTimeMillis()); } private long popTime() { if (!stack.empty()) { Long start = (Long) stack.pop(); return start; } else { return 0; } } /** * Creates and returns a logger with a name based on the target object. * Allows controlling trace logging through enabling loggers in logging config. * <p/> * Performance: It is assumed that the underlying log implementation uses a * caching mechanism (logback does). * * @param jp * @return */ private Logger getLogger(JoinPoint.StaticPart jp) { Class<?> key = jp.getSignature().getDeclaringType(); return LoggerFactory.getLogger("trace." + key.getName()); } private String indent(int i, CharSequence s) { return MyStringBuilder.fixedLengthString(s, i); } public static class MyStringBuilder { public static String fixedLengthString(CharSequence s, int length) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { sb.append(s); } return sb.toString(); } } }