Java: Is it possible to automatically add log stat

2019-06-21 20:57发布

Most of the methods in my application are written like this:

public void m() {
    long t1 = System.currentTimeMillis();
    log.info("begin - m()");

    /* method body */

    long t2 = System.currentTimeMillis();
    log.info("end - m(), took " + (t2 - t1) + "ms.");
}

I wish I could simply annotate my method and have the log statements be automagically generated instead:

@Log("executionTime")
public void m() {
    /* method body */
}

Any ideas on how to proceed with this approach ? Is there any known solution ?

Someone suggested AOP for this. The problem is that with AspectJ or Spring AOP I would have to describe all the methods which ammounts to as much code as the log calls in the method itself.

标签: java logging aop
9条回答
男人必须洒脱
2楼-- · 2019-06-21 21:33

While this is not an actual practical answer to your question just yet (some good answers have been with respect to AOP), I believe the concept of ARM in Java 7 should be a viable option for implementing something like this on a small scale.

You could define a utility logging class and a factory to produce that class, something like the following:

public class TimerFactory
{
    private static final Logger log = ...; // Obtain however

    static class Timer implements Disposable<RuntimeException>
    {
        private final long startTime;
        private final String name;

        private Timer(String name)
        {
           this.name = name;
           startTime= System.currentTimeMillis();
           log.info("begin - " + name);
        }

        public void close()
        {
           final long t2 = System.currentTimeMillis();
           log.info("end - " + name + ", took " + (t2 - t1) + "ms.");
        }
    }

    public static Timer getTimer(String name)
    {
       return new Timer(name);
    }
}

Now with that boilerplate out of the way (essentially an encapsulation of your logging behaviour), it could be called as follows:

public void m() {
   try (TimerFactory.getTimer("m()")) {

      /* method body */

   }
}

The first log method would be called at the entrance to the try block, and the start time recorded. When the try block was exited, the resource (the Timer in this case) would automatically be "closed", which would cause the final time to be calculated and logged. Note as well that because this is a try block, the end logging will happen regardless of whether an exception is thrown or not. Your original code should probably use a try-finally block to ensure that the logging is actually completed.

Obviously this still requires you to put some logging code at each site, so is not really a replacement for clever pointcuts and AOP, even once Java 7 is released. However, if you find yourself dropping the logging in every now and then into perhaps a handful of methods, this pattern is a good way to abstract out the logging concerns and allow you to reuse it with the minimum of boilerplate.

查看更多
Summer. ? 凉城
3楼-- · 2019-06-21 21:38

Comment out the logging or profiling calls with a unique search term:

void myfunc() {

  //log-call-123: try { long log_t1 = System.currentTimeMillis();        
  //log-call-123: log.info("begin - myfunc()"); {

  ...
  normal method code
  ...

  //log-call-123: } } finally { long log_t2 = System.currentTimeMillis();         
  //log-call-123: log.info("end - myfunc(), took " + (log_t2 - log_t1) + "ms."); }

}

When you search and replace:

Search for: "//log-call-123:"

Replace with: "/* log-call-123 */"

Do the reverse search and replace when you want to turn off extra logging or profiling calls.

查看更多
Deceive 欺骗
4楼-- · 2019-06-21 21:43

AspectJ and Spring AOP support something like:

execution(* com.company.project..*.*(..))

which will cover all methods in all sub-packages of project. So no need to define all methods one by one.

查看更多
该账号已被封号
5楼-- · 2019-06-21 21:43

CGLIB let you modify method code at runtime

查看更多
smile是对你的礼貌
6楼-- · 2019-06-21 21:45

You should use an aspect to this requirement. This requirement is a crosscuting concern (a concern that "cuts" between many classes).

To capture the methods that you want match you should create a pointcut that matches one or more join points. A join point is something that can be executed on your code (a method for example).

Look at this simple examples about tracing and logging and at this link about wildcards and pointcuts.

查看更多
男人必须洒脱
7楼-- · 2019-06-21 21:47

AspectJ has the concept of a join point, which is like a wildcard that can specify any methods that match that wildcard (you can specify particular methods in a class or any class that matches the wildcard). Then you can create an aspect which contains before advice and after advice, which are methods that run before and after the method matched by the join point. You can generate your log methods this way.

查看更多
登录 后发表回答