Java: как профилировать один запрос в производстве - PullRequest
0 голосов
/ 25 октября 2018

Я ищу что-то вроде профайлера опроса Jvisualvm, который можно использовать на производстве для профилирования одного вызова метода.

Я не могу использовать visualvm на производстве или любые другие внешние инструменты, доступные для рядапричины.

Я ищу код Java, который я мог бы добавить в свой код для профилирования вызова определенного метода.

Я не могу найти ничего, что делает это без использования внешнего инструмента.

1 Ответ

0 голосов
/ 25 октября 2018

Я подумал, что поделюсь своим решением здесь, если у кого-нибудь есть лучшее, поделитесь, пожалуйста.

Это Аспект, но его также можно использовать напрямую вот так ...

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;

            }
        }
    }

}
...