View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.tools.logging.slf4jlogback;
12  
13  import java.lang.reflect.InvocationTargetException;
14  import java.lang.reflect.Method;
15  import java.util.ArrayList;
16  import java.util.List;
17  
18  import org.apache.commons.lang3.reflect.MethodUtils;
19  
20  import psiprobe.tools.logging.DefaultAccessor;
21  
22  /**
23   * Wraps a TomcatSlf4jLogback logger factory from a given web application class loader.
24   *
25   * <p>
26   * All TomcatSlf4jLogback classes are loaded via the given class loader and not via psi-probe's own
27   * class loader. For this reasons, all methods on TomcatSlf4jLogback objects are invoked via
28   * reflection.
29   * </p>
30   * <p>
31   * This way, we can even handle different versions of TomcatSlf4jLogback embedded in different WARs.
32   * </p>
33   */
34  public class TomcatSlf4jLogbackFactoryAccessor extends DefaultAccessor {
35  
36    /**
37     * Attempts to initialize a TomcatSlf4jLogback logger factory via the given class loader.
38     *
39     * @param cl the ClassLoader to use when fetching the factory
40     *
41     * @throws ClassNotFoundException the class not found exception
42     * @throws IllegalAccessException the illegal access exception
43     * @throws InvocationTargetException the invocation target exception
44     */
45    public TomcatSlf4jLogbackFactoryAccessor(ClassLoader cl)
46        throws ClassNotFoundException, IllegalAccessException, InvocationTargetException {
47  
48      // Get the singleton SLF4J binding, which may or may not be Logback, depending on the binding.
49      Class<?> clazz = cl.loadClass("org.apache.juli.logging.org.slf4j.impl.StaticLoggerBinder");
50      Method getSingleton = MethodUtils.getAccessibleMethod(clazz, "getSingleton");
51      Object singleton = getSingleton.invoke(null);
52      Method getLoggerFactory = MethodUtils.getAccessibleMethod(clazz, "getLoggerFactory");
53  
54      Object loggerFactory = getLoggerFactory.invoke(singleton);
55  
56      // Check if the binding is indeed Logback
57      Class<?> loggerFactoryClass =
58          cl.loadClass("org.apache.juli.logging.ch.qos.logback.classic.LoggerContext");
59      if (!loggerFactoryClass.isInstance(loggerFactory)) {
60        throw new RuntimeException("The singleton SLF4J binding was not Logback");
61      }
62      setTarget(loggerFactory);
63    }
64  
65    /**
66     * Returns the TomcatSlf4jLogback root logger.
67     *
68     * @return the root logger
69     */
70    public TomcatSlf4jLogbackLoggerAccessor getRootLogger() {
71      /*
72       * TomcatSlf4jLogback has no dedicated getRootLogger() method, so we simply access the root
73       * logger by its well-defined name.
74       */
75      return getLogger("ROOT");
76    }
77  
78    /**
79     * Returns the TomcatSlf4jLogback logger with a given name.
80     *
81     * @param name the name
82     *
83     * @return the Logger with the given name
84     */
85    public TomcatSlf4jLogbackLoggerAccessor getLogger(String name) {
86      try {
87        Class<? extends Object> clazz = getTarget().getClass();
88        Method getLogger = MethodUtils.getAccessibleMethod(clazz, "getLogger", String.class);
89  
90        Object logger = getLogger.invoke(getTarget(), name);
91        if (logger == null) {
92          throw new NullPointerException(getTarget() + ".getLogger(\"" + name + "\") returned null");
93        }
94        TomcatSlf4jLogbackLoggerAccessor accessor = new TomcatSlf4jLogbackLoggerAccessor();
95        accessor.setTarget(logger);
96        accessor.setApplication(getApplication());
97        return accessor;
98  
99      } catch (Exception e) {
100       logger.error("{}.getLogger('{}') failed", getTarget(), name, e);
101     }
102     return null;
103   }
104 
105   /**
106    * Returns a list of wrappers for all TomcatSlf4jLogback appenders that have an associated logger.
107    *
108    * @return a list of {@link TomcatSlf4jLogbackAppenderAccessor}s representing all appenders that
109    *         are in use
110    */
111   @SuppressWarnings("unchecked")
112   public List<TomcatSlf4jLogbackAppenderAccessor> getAppenders() {
113     List<TomcatSlf4jLogbackAppenderAccessor> appenders = new ArrayList<>();
114     try {
115       Class<? extends Object> clazz = getTarget().getClass();
116       Method getLoggerList = MethodUtils.getAccessibleMethod(clazz, "getLoggerList");
117 
118       List<Object> loggers = (List<Object>) getLoggerList.invoke(getTarget());
119       for (Object logger : loggers) {
120         TomcatSlf4jLogbackLoggerAccessor accessor = new TomcatSlf4jLogbackLoggerAccessor();
121         accessor.setTarget(logger);
122         accessor.setApplication(getApplication());
123 
124         appenders.addAll(accessor.getAppenders());
125       }
126     } catch (Exception e) {
127       logger.error("{}.getLoggerList() failed", getTarget(), e);
128     }
129     return appenders;
130   }
131 
132 }