Я подумал, что поделюсь своим решением здесь, если у кого-нибудь есть лучшее, поделитесь, пожалуйста.
Это Аспект, но его также можно использовать напрямую вот так ...
void test()
{
Profiler profiler = new Profiler(Thread.currentThread(), joinpoint.getTarget().getClass().getSimpleName());
// call some methods here
profiler.dump();
}
Это класс Profiler.
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.google.common.base.Stopwatch;
import au.com.noojee.aspects.AspectProfile.Tree.Node;
import au.com.noojee.aspects.annotations.AopProfile;
@Aspect
public class AspectProfile
{
@Pointcut("execution(@au.com.noojee.aspects.annotations.AopProfile * *.*(..))")
public void serviceMethods()
{
}
@Around("serviceMethods()")
public Object aroundMethod(final ProceedingJoinPoint joinpoint) throws Throwable
{
MethodSignature signature = (MethodSignature) joinpoint.getSignature();
Method method = signature.getMethod();
AopProfile myAnnotation = method.getAnnotation(AopProfile.class);
long minTimeToLog = myAnnotation.minimumMs();
Stopwatch timer = Stopwatch.createStarted();
Profiler profiler = new Profiler(Thread.currentThread(), joinpoint.getTarget().getClass().getSimpleName());
try
{
return joinpoint.proceed();
}
finally
{
try
{
if (timer.elapsed(TimeUnit.MILLISECONDS) > minTimeToLog)
{
LoggerFactoryForAspects.getLogger().warn("Invokation of {}.{}(...) took {}ms",
joinpoint.getTarget().getClass().getSimpleName(), joinpoint.getSignature().getName(),
timer.elapsed(TimeUnit.MILLISECONDS));
profiler.dump();
}
}
catch (Exception e)
{
LoggerFactoryForAspects.getLogger().error(e, e);
}
}
}
public class Profiler implements Runnable
{
private final String name;
private final Thread threadToProfile;
private final Tree<Call> calls = new Tree<Call>(new Call());
private final ScheduledFuture<?> future;
Profiler(Thread threadToProfile, String name)
{
this.threadToProfile = threadToProfile;
this.name = name;
// future = Executor.scheduler.scheduleAtFixedRate(this, 100, 100,
// TimeUnit.MILLISECONDS);
future = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(this, 100, 100, TimeUnit.MILLISECONDS);
}
public void dump()
{
future.cancel(false);
dumpNode(calls.root, "");
}
void dumpNode(Node<Call> node, String indent)
{
LoggerFactoryForAspects.getLogger().warn("'{}' {} [ {} ] {}", name, indent, node.data.count,
node.data.name);
if (node.children.size() > 1)
{
LoggerFactoryForAspects.getLogger().warn("'{}' {} children: {}", name, indent, node.children.size());
}
for (Node<Call> child : node.children)
{
String ch = " ";
if (node.children.size() > 1)
{
ch = "+";
}
dumpNode(child, indent + ch);
}
}
class Call implements Comparable<Call>
{
int count = 1;
String name;
@Override
public int compareTo(Call o)
{
return count - o.count;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Call other = (Call) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
private Profiler getOuterType()
{
return Profiler.this;
}
}
@Override
public void run()
{
Node<Call> node = calls.root;
List<StackTraceElement> elements = new LinkedList<>();
for (StackTraceElement element : threadToProfile.getStackTrace())
{
elements.add(element);
}
Collections.reverse(elements);
for (StackTraceElement trace : elements)
{
Call call = new Call();
call.name = trace.toString();
if (!call.name.startsWith("java") && !call.name.startsWith("sun") && !call.name.startsWith("org")
&& !call.name.startsWith("com."))
{
node = node.findOrCreateChild(call);
}
}
node.data.count++;
}
}
public static class Tree<T>
{
private final Node<T> root;
public Tree(T rootData)
{
root = new Node<T>();
root.data = rootData;
}
public static class Node<T>
{
private T data;
// private Node<T> parent;
private List<Node<T>> children = new LinkedList<Node<T>>();
Node<T> findOrCreateChild(T target)
{
for (Node<T> node : children)
{
if (node.data.equals(target))
{
return node;
}
}
return addChild(target);
}
Node<T> addChild(T value)
{
Node<T> node = new Node<>();
node.data = value;
// node.parent = this;
children.add(node);
return node;
}
}
}
}