Search code examples
javaaopaspectj

Accessing private static members (e.g. loggers) from AspectJ


Suppose that I have this class

public class MyClass {
    private Logger log = LogFactory.getLogger(MyClass.class);

    public void doSomething() {
        // doing something
    }
}

Suppose that I want to write an aspect to log entry and exit:

public aspect TraceAspect {

    pointcut method(): execution(* *(..));

    before(): method(){

        log.info("entering method");
    }

    after(): method(){
        log.info("existing method");
    }
}

The problem here is that I want to access the log object inside the class with aspect, but I don't know how. I don't want to create a new logger because I want to keep all the data associated with the class logger when logging. Is there a way or a pattern to access class data?

EDIT: wanted to state that this aspect should trace all classes that have log field. That is, I might have many classes: MyClass, MyClass1, YourClass2, RepositoryClass, etc.


Solution

  • What Nándor said is technically correct, but my advice is: Please avoid accessing private members or methods whenever you can because they are private for a reason. For instance, they are subject to change even when the public interface of a class does not change. So you can never rely on their existence or their naming. Furthermore, a cross-cutting concern should also comply with design principles like encapsulation as much as possible.

    This specific case is about Slf4J loggers, more precisely about your wish to avoid creating redundant logger objects. Well, Slf4J is not as stupid or careless as you might think concerning object creation. All classes implementing ILoggerFactory use an internal map in order to cache existing loggers for given (class) names, see e.g.

    So why don't you just relax and access the corresponding loggers from your aspect using the target class names. This even works if a target class does not have its own static logger:

    Application classes:

    package de.scrum_master.app;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class MyClassWithLogger {
      private Logger log = LoggerFactory.getLogger(MyClassWithLogger.class);
    
      public void doSomething() {}
    }
    
    package de.scrum_master.app;
    
    public class MyClassWithoutLogger {
      public void doSomething() {}
    }
    
    package de.scrum_master.app;
    
    public class Application {
      public static void main(String[] args) {
        new MyClassWithLogger().doSomething();
        new MyClassWithoutLogger().doSomething();
      }
    }
    

    Aspect:

    package de.scrum_master.aspect;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public aspect TraceAspect {
      pointcut executedMethods(Object targetObject) :
        execution(!static * *(..)) && target(targetObject);
    
      before(Object targetObject) : executedMethods(targetObject) {
        Logger log = LoggerFactory.getLogger(targetObject.getClass());
        log.info("Entering " + thisJoinPoint);
      }
    
      after(Object targetObject) : executedMethods(targetObject) {
        Logger log = LoggerFactory.getLogger(targetObject.getClass());
        log.info("Exiting " + thisJoinPoint);
      }
    }
    

    Console log with Slf4J configured to use simple logger:

    [main] INFO de.scrum_master.app.MyClassWithLogger - Entering execution(void de.scrum_master.app.MyClassWithLogger.doSomething())
    [main] INFO de.scrum_master.app.MyClassWithLogger - Exiting execution(void de.scrum_master.app.MyClassWithLogger.doSomething())
    [main] INFO de.scrum_master.app.MyClassWithoutLogger - Entering execution(void de.scrum_master.app.MyClassWithoutLogger.doSomething())
    [main] INFO de.scrum_master.app.MyClassWithoutLogger - Exiting execution(void de.scrum_master.app.MyClassWithoutLogger.doSomething())