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.beans;
12  
13  import jakarta.inject.Inject;
14  import jakarta.servlet.ServletContext;
15  
16  import java.io.File;
17  import java.io.Serializable;
18  import java.lang.reflect.InvocationTargetException;
19  import java.lang.reflect.Method;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.catalina.Context;
28  import org.apache.catalina.Loader;
29  import org.apache.commons.lang3.reflect.MethodUtils;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  import org.springframework.beans.factory.annotation.Autowired;
33  import org.springframework.util.ClassUtils;
34  
35  import psiprobe.model.Application;
36  import psiprobe.model.DisconnectedLogDestination;
37  import psiprobe.tools.ApplicationUtils;
38  import psiprobe.tools.Instruments;
39  import psiprobe.tools.logging.FileLogAccessor;
40  import psiprobe.tools.logging.LogDestination;
41  import psiprobe.tools.logging.catalina.CatalinaLoggerAccessor;
42  import psiprobe.tools.logging.commons.CommonsLoggerAccessor;
43  import psiprobe.tools.logging.jdk.Jdk14LoggerAccessor;
44  import psiprobe.tools.logging.jdk.Jdk14ManagerAccessor;
45  import psiprobe.tools.logging.log4j.Log4JLoggerAccessor;
46  import psiprobe.tools.logging.log4j.Log4JManagerAccessor;
47  import psiprobe.tools.logging.log4j2.Log4J2AppenderAccessor;
48  import psiprobe.tools.logging.log4j2.Log4J2LoggerConfigAccessor;
49  import psiprobe.tools.logging.log4j2.Log4J2LoggerContextAccessor;
50  import psiprobe.tools.logging.log4j2.Log4J2WebLoggerContextUtilsAccessor;
51  import psiprobe.tools.logging.logback.LogbackFactoryAccessor;
52  import psiprobe.tools.logging.logback.LogbackLoggerAccessor;
53  import psiprobe.tools.logging.logback13.Logback13FactoryAccessor;
54  import psiprobe.tools.logging.logback13.Logback13LoggerAccessor;
55  import psiprobe.tools.logging.slf4jlogback.TomcatSlf4jLogbackFactoryAccessor;
56  import psiprobe.tools.logging.slf4jlogback.TomcatSlf4jLogbackLoggerAccessor;
57  import psiprobe.tools.logging.slf4jlogback13.TomcatSlf4jLogback13FactoryAccessor;
58  import psiprobe.tools.logging.slf4jlogback13.TomcatSlf4jLogback13LoggerAccessor;
59  
60  /**
61   * The Class LogResolverBean.
62   */
63  public class LogResolverBean {
64  
65    /** The Constant logger. */
66    private static final Logger logger = LoggerFactory.getLogger(LogResolverBean.class);
67  
68    /** The container wrapper. */
69    @Inject
70    private ContainerWrapperBean containerWrapper;
71  
72    /** The stdout files. */
73    private List<String> stdoutFiles = new ArrayList<>();
74  
75    /**
76     * Gets the container wrapper.
77     *
78     * @return the container wrapper
79     */
80    public ContainerWrapperBean getContainerWrapper() {
81      return containerWrapper;
82    }
83  
84    /**
85     * Sets the container wrapper.
86     *
87     * @param containerWrapper the new container wrapper
88     */
89    public void setContainerWrapper(ContainerWrapperBean containerWrapper) {
90      this.containerWrapper = containerWrapper;
91    }
92  
93    /**
94     * Gets the stdout files.
95     *
96     * @return the stdout files
97     */
98    public List<String> getStdoutFiles() {
99      return stdoutFiles;
100   }
101 
102   /**
103    * Sets the stdout files.
104    *
105    * @param stdoutFiles the new stdout files
106    */
107   @Autowired
108   public void setStdoutFiles(List<String> stdoutFiles) {
109     logger.info("stdoutFiles {}", stdoutFiles);
110     this.stdoutFiles = stdoutFiles;
111   }
112 
113   /**
114    * Gets the log destinations.
115    *
116    * @param all the all
117    *
118    * @return the log destinations
119    */
120   public List<LogDestination> getLogDestinations(boolean all) {
121     List<LogDestination> allAppenders = getAllLogDestinations();
122 
123     if (allAppenders.isEmpty()) {
124       return Collections.emptyList();
125     }
126 
127     //
128     // this list has to guarantee the order in which elements are added
129     //
130     List<LogDestination> uniqueList = new LinkedList<>();
131     AbstractLogComparator cmp = new LogDestinationComparator(all);
132 
133     Collections.sort(allAppenders, cmp);
134     for (LogDestination dest : allAppenders) {
135       if (Collections.binarySearch(uniqueList, dest, cmp) < 0
136           && (all || dest.getFile() == null || dest.getFile().exists())) {
137         uniqueList.add(new DisconnectedLogDestination().builder(dest));
138       }
139     }
140     return uniqueList;
141   }
142 
143   /**
144    * Gets the log sources.
145    *
146    * @param logFile the log file
147    *
148    * @return the log sources
149    */
150   public List<LogDestination> getLogSources(File logFile) {
151     List<LogDestination> filtered = new LinkedList<>();
152     List<LogDestination> sources = getLogSources();
153     for (LogDestination dest : sources) {
154       if (logFile.equals(dest.getFile())) {
155         filtered.add(dest);
156       }
157     }
158     return filtered;
159   }
160 
161   /**
162    * Gets the log sources.
163    *
164    * @return the log sources
165    */
166   public List<LogDestination> getLogSources() {
167     List<LogDestination> sources = new LinkedList<>();
168 
169     List<LogDestination> allAppenders = getAllLogDestinations();
170     if (!allAppenders.isEmpty()) {
171       AbstractLogComparator cmp = new LogSourceComparator();
172 
173       Collections.sort(allAppenders, cmp);
174       for (LogDestination dest : allAppenders) {
175         if (Collections.binarySearch(sources, dest, cmp) < 0) {
176           sources.add(new DisconnectedLogDestination().builder(dest));
177         }
178       }
179     }
180     return sources;
181   }
182 
183   /**
184    * Gets the all log destinations.
185    *
186    * @return the all log destinations
187    */
188   private List<LogDestination> getAllLogDestinations() {
189     if (!Instruments.isInitialized()) {
190       return Collections.emptyList();
191     }
192 
193     List<LogDestination> allAppenders = new ArrayList<>();
194 
195     //
196     // interrogate classloader hierarchy
197     //
198     ClassLoader cl2 = Thread.currentThread().getContextClassLoader().getParent();
199     while (cl2 != null) {
200       interrogateClassLoader(cl2, null, allAppenders);
201       cl2 = cl2.getParent();
202     }
203 
204     //
205     // check for known stdout files, such as "catalina.out"
206     //
207     interrogateStdOutFiles(allAppenders);
208 
209     //
210     // interrogate webapp classloaders and available loggers
211     //
212     List<Context> contexts = getContainerWrapper().getTomcatContainer().findContexts();
213     for (Context ctx : contexts) {
214       interrogateContext(ctx, allAppenders);
215     }
216 
217     return allAppenders;
218   }
219 
220   /**
221    * Gets the log destination.
222    *
223    * @param logType the log type
224    * @param webapp the webapp
225    * @param context the context
226    * @param root the root
227    * @param logName the log name
228    * @param logIndex the log index
229    *
230    * @return the log destination
231    */
232   public LogDestination getLogDestination(String logType, String webapp, boolean context,
233       boolean root, String logName, String logIndex) {
234 
235     LogDestination result = null;
236     Context ctx = null;
237     Application application = null;
238     if (webapp != null) {
239       ctx = getContainerWrapper().getTomcatContainer().findContext(webapp);
240       if (ctx != null) {
241         application = ApplicationUtils.getApplication(ctx, getContainerWrapper());
242       }
243     }
244 
245     // Supported loggers
246     List<String> loggers = new ArrayList<>();
247     loggers.add("jdk");
248     loggers.add("log4j");
249     loggers.add("log4j2");
250     loggers.add("logback");
251     loggers.add("logback13");
252     loggers.add("tomcatSlf4jLogback");
253     loggers.add("tomcatSlf4jLogback13");
254 
255     if (logName != null && "stdout".equals(logType)) {
256       result = getStdoutLogDestination(logName);
257     } else if (ctx != null && "catalina".equals(logType)) {
258       result = getCatalinaLogDestination(ctx, application);
259     } else if (logIndex != null && loggers.contains(logType)) {
260       if (context && ctx != null && !"log4j2".equals(logType)) {
261         result = getCommonsLogDestination(ctx, application, logIndex);
262       } else if (ctx != null && "log4j2".equals(logType)) {
263         result = getLog4J2LogDestination(ctx, application, root, logName, logIndex);
264       } else {
265         ClassLoader cl;
266         ClassLoader prevCl = null;
267         if (ctx != null) {
268           cl = ctx.getLoader().getClassLoader();
269           prevCl = ClassUtils.overrideThreadContextClassLoader(cl);
270         } else {
271           cl = Thread.currentThread().getContextClassLoader().getParent();
272         }
273         try {
274           if (root || logName != null) {
275             if ("jdk".equals(logType)) {
276               result = getJdk14LogDestination(cl, application, root, logName, logIndex);
277             } else if ("log4j".equals(logType)) {
278               result = getLog4JLogDestination(cl, application, root, logName, logIndex);
279             } else if ("logback".equals(logType)) {
280               result = getLogbackLogDestination(cl, application, root, logName, logIndex);
281             } else if ("logback13".equals(logType)) {
282               result = getLogback13LogDestination(cl, application, root, logName, logIndex);
283             } else if ("tomcatSlf4jLogback".equals(logType)) {
284               result = getLogbackTomcatJuliLogDestination(cl, application, root, logName, logIndex);
285             } else if ("tomcatSlf4jLogback13".equals(logType)) {
286               result =
287                   getLogback13TomcatJuliLogDestination(cl, application, root, logName, logIndex);
288             }
289           }
290         } finally {
291           if (prevCl != null) {
292             ClassUtils.overrideThreadContextClassLoader(prevCl);
293           }
294         }
295       }
296     }
297     return result;
298   }
299 
300   /**
301    * Interrogate context.
302    *
303    * @param ctx the ctx
304    * @param allAppenders the all appenders
305    */
306   private void interrogateContext(Context ctx, List<LogDestination> allAppenders) {
307     Application application = ApplicationUtils.getApplication(ctx, getContainerWrapper());
308     ClassLoader cl = ctx.getLoader().getClassLoader();
309     Object contextLogger = ctx.getLogger();
310     if (contextLogger != null) {
311       if (contextLogger.getClass().getName().startsWith("org.apache.commons.logging")) {
312         CommonsLoggerAccessor commonsAccessor = new CommonsLoggerAccessor();
313         commonsAccessor.setTarget(contextLogger);
314         commonsAccessor.setApplication(application);
315         allAppenders.addAll(commonsAccessor.getDestinations());
316       } else if (contextLogger.getClass().getName().startsWith("org.apache.catalina.logger")) {
317         CatalinaLoggerAccessor catalinaAccessor = new CatalinaLoggerAccessor();
318         catalinaAccessor.setApplication(application);
319         catalinaAccessor.setTarget(contextLogger);
320         allAppenders.add(catalinaAccessor);
321       }
322 
323       ServletContext servletContext = ctx.getServletContext();
324       try {
325         Log4J2LoggerContextAccessor loggerContextAccessor = null;
326         try {
327           Log4J2WebLoggerContextUtilsAccessor webLoggerContextUtilsAccessor =
328               new Log4J2WebLoggerContextUtilsAccessor(cl);
329           loggerContextAccessor = webLoggerContextUtilsAccessor.getWebLoggerContext(servletContext);
330         } catch (Exception e) {
331           logger.debug("Log4J2LoggerContextAccessor instantiation failed", e);
332         }
333         List<Object> loggerContexts = getLoggerContexts(cl);
334         for (Object loggerContext : loggerContexts) {
335           Map<String, Object> loggerConfigs = getLoggerConfigs(loggerContext);
336           for (Object loggerConfig : loggerConfigs.values()) {
337             Log4J2LoggerConfigAccessor logConfigAccessor = new Log4J2LoggerConfigAccessor();
338             logConfigAccessor.setTarget(loggerConfig);
339             logConfigAccessor.setApplication(application);
340             logConfigAccessor.setContext(true);
341             logConfigAccessor.setLoggerContext(loggerContextAccessor);
342             Method getAppenders =
343                 MethodUtils.getAccessibleMethod(loggerConfig.getClass(), "getAppenders");
344             @SuppressWarnings("unchecked")
345             Map<String, Object> appenders = (Map<String, Object>) getAppenders.invoke(loggerConfig);
346             for (Object appender : appenders.values()) {
347               Log4J2AppenderAccessor appenderAccessor = new Log4J2AppenderAccessor();
348               appenderAccessor.setTarget(appender);
349               appenderAccessor.setLoggerAccessor(logConfigAccessor);
350               appenderAccessor.setApplication(application);
351               allAppenders.add(appenderAccessor);
352             }
353           }
354         }
355       } catch (Exception e) {
356         logger.debug("getting appenders failed", e);
357       }
358     }
359 
360     if (application.isAvailable()) {
361       ClassLoader prevCl = ClassUtils.overrideThreadContextClassLoader(cl);
362       try {
363         interrogateClassLoader(cl, application, allAppenders);
364       } catch (Exception e) {
365         logger.error(
366             "Could not interrogate classloader loggers for {}. Enable debug logging to see the trace stack",
367             ctx.getName());
368         logger.debug("", e);
369       } finally {
370         if (prevCl != null) {
371           ClassUtils.overrideThreadContextClassLoader(prevCl);
372         }
373       }
374     }
375   }
376 
377   /**
378    * Interrogate class loader.
379    *
380    * @param cl the cl
381    * @param application the application
382    * @param appenders the appenders
383    */
384   private void interrogateClassLoader(ClassLoader cl, Application application,
385       List<LogDestination> appenders) {
386 
387     String applicationName =
388         application != null ? "application \"" + application.getName() + "\"" : "server";
389 
390     // check for JDK loggers
391     try {
392       Jdk14ManagerAccessor jdk14accessor = new Jdk14ManagerAccessor(cl);
393       jdk14accessor.setApplication(application);
394       appenders.addAll(jdk14accessor.getHandlers());
395     } catch (Exception e) {
396       logger.debug("Could not resolve JDK loggers for '{}'", applicationName, e);
397     }
398 
399     // check for Log4J loggers
400     try {
401       Log4JManagerAccessor log4JAccessor = new Log4JManagerAccessor(cl);
402       log4JAccessor.setApplication(application);
403       appenders.addAll(log4JAccessor.getAppenders());
404     } catch (Exception e) {
405       logger.debug("Could not resolve log4j loggers for '{}'", applicationName, e);
406     }
407 
408     // check for Logback loggers
409     try {
410       LogbackFactoryAccessor logbackAccessor = new LogbackFactoryAccessor(cl);
411       logbackAccessor.setApplication(application);
412       appenders.addAll(logbackAccessor.getAppenders());
413     } catch (Exception e) {
414       logger.debug("Could not resolve logback loggers for '{}'", applicationName, e);
415     }
416 
417     // check for Logback 1.3 loggers
418     try {
419       Logback13FactoryAccessor logback13Accessor = new Logback13FactoryAccessor(cl);
420       logback13Accessor.setApplication(application);
421       appenders.addAll(logback13Accessor.getAppenders());
422     } catch (Exception e) {
423       logger.debug("Could not resolve logback 1.3 loggers for '{}'", applicationName, e);
424     }
425 
426     // check for tomcat-slf4j-logback loggers
427     try {
428       TomcatSlf4jLogbackFactoryAccessor tomcatSlf4jLogbackAccessor =
429           new TomcatSlf4jLogbackFactoryAccessor(cl);
430       tomcatSlf4jLogbackAccessor.setApplication(application);
431       appenders.addAll(tomcatSlf4jLogbackAccessor.getAppenders());
432     } catch (Exception e) {
433       logger.debug("Could not resolve tomcat-slf4j-logback loggers for '{}'", applicationName, e);
434     }
435 
436     // check for tomcat-slf4j-logback 1.3 loggers
437     try {
438       TomcatSlf4jLogback13FactoryAccessor tomcatSlf4jLogback13Accessor =
439           new TomcatSlf4jLogback13FactoryAccessor(cl);
440       tomcatSlf4jLogback13Accessor.setApplication(application);
441       appenders.addAll(tomcatSlf4jLogback13Accessor.getAppenders());
442     } catch (Exception e) {
443       logger.debug("Could not resolve tomcat-slf4j-logback 1.3 loggers for '{}'", applicationName,
444           e);
445     }
446   }
447 
448   /**
449    * Interrogate std out files.
450    *
451    * @param appenders the appenders
452    */
453   private void interrogateStdOutFiles(List<LogDestination> appenders) {
454     for (String fileName : stdoutFiles) {
455       FileLogAccessor fla = resolveStdoutLogDestination(fileName);
456       if (fla != null) {
457         appenders.add(fla);
458       }
459     }
460   }
461 
462   /**
463    * Gets the stdout log destination.
464    *
465    * @param logName the log name
466    *
467    * @return the stdout log destination
468    */
469   private LogDestination getStdoutLogDestination(String logName) {
470     for (String fileName : stdoutFiles) {
471       if (fileName.equals(logName)) {
472         FileLogAccessor fla = resolveStdoutLogDestination(fileName);
473         if (fla != null) {
474           return fla;
475         }
476       }
477     }
478     return null;
479   }
480 
481   /**
482    * Resolve stdout log destination.
483    *
484    * @param fileName the file name
485    *
486    * @return the file log accessor
487    */
488   private FileLogAccessor resolveStdoutLogDestination(String fileName) {
489     File stdout = new File(System.getProperty("catalina.base"), "logs/" + fileName);
490     if (stdout.exists()) {
491       FileLogAccessor fla = new FileLogAccessor();
492       fla.setName(fileName);
493       fla.setFile(stdout);
494       return fla;
495     }
496     return null;
497   }
498 
499   /**
500    * Gets the catalina log destination.
501    *
502    * @param ctx the ctx
503    * @param application the application
504    *
505    * @return the catalina log destination
506    */
507   private LogDestination getCatalinaLogDestination(Context ctx, Application application) {
508     Object log = ctx.getLogger();
509     if (log != null) {
510       CatalinaLoggerAccessor logAccessor = new CatalinaLoggerAccessor();
511       logAccessor.setTarget(log);
512       logAccessor.setApplication(application);
513       if (logAccessor.getFile().exists()) {
514         return logAccessor;
515       }
516     }
517     return null;
518   }
519 
520   /**
521    * Gets the commons log destination.
522    *
523    * @param ctx the ctx
524    * @param application the application
525    * @param logIndex the log index
526    *
527    * @return the commons log destination
528    */
529   private LogDestination getCommonsLogDestination(Context ctx, Application application,
530       String logIndex) {
531     Object contextLogger = ctx.getLogger();
532     CommonsLoggerAccessor commonsAccessor = new CommonsLoggerAccessor();
533     commonsAccessor.setTarget(contextLogger);
534     commonsAccessor.setApplication(application);
535     return commonsAccessor.getDestination(logIndex);
536   }
537 
538   /**
539    * Gets the jdk14 log destination.
540    *
541    * @param cl the cl
542    * @param application the application
543    * @param root the root
544    * @param logName the log name
545    * @param handlerIndex the handler index
546    *
547    * @return the jdk14 log destination
548    */
549   private LogDestination getJdk14LogDestination(ClassLoader cl, Application application,
550       boolean root, String logName, String handlerIndex) {
551 
552     try {
553       Jdk14ManagerAccessor manager = new Jdk14ManagerAccessor(cl);
554       manager.setApplication(application);
555       Jdk14LoggerAccessor log = root ? manager.getRootLogger() : manager.getLogger(logName);
556       if (log != null) {
557         return log.getHandler(handlerIndex);
558       }
559     } catch (Exception e) {
560       logger.debug("getJdk14LogDestination failed", e);
561     }
562     return null;
563   }
564 
565   /**
566    * Gets the log4j log destination.
567    *
568    * @param cl the cl
569    * @param application the application
570    * @param root the root
571    * @param logName the log name
572    * @param appenderName the appender name
573    *
574    * @return the log4j log destination
575    */
576   private LogDestination getLog4JLogDestination(ClassLoader cl, Application application,
577       boolean root, String logName, String appenderName) {
578 
579     try {
580       Log4JManagerAccessor manager = new Log4JManagerAccessor(cl);
581       manager.setApplication(application);
582       Log4JLoggerAccessor log = root ? manager.getRootLogger() : manager.getLogger(logName);
583       if (log != null) {
584         return log.getAppender(appenderName);
585       }
586     } catch (Exception e) {
587       logger.debug("getLog4JLogDestination failed", e);
588     }
589     return null;
590   }
591 
592   /**
593    * Gets the log4j2 log destination.
594    *
595    * @param ctx the ctx
596    * @param application the application
597    * @param root the root
598    * @param logName the log name
599    * @param appenderName the appender name
600    *
601    * @return the log4j2 log destination
602    */
603   private LogDestination getLog4J2LogDestination(Context ctx, Application application, boolean root,
604       String logName, String appenderName) {
605 
606     Log4J2AppenderAccessor result = null;
607     try {
608       Loader loader = ctx.getLoader();
609       ClassLoader classLoader = loader.getClassLoader();
610       Log4J2WebLoggerContextUtilsAccessor webLoggerContextUtilsAccessor =
611           new Log4J2WebLoggerContextUtilsAccessor(classLoader);
612       Log4J2LoggerContextAccessor loggerContextAccessor =
613           webLoggerContextUtilsAccessor.getWebLoggerContext(ctx.getServletContext());
614       List<Object> loggerContexts = getLoggerContexts(classLoader);
615       Object loggerConfig = null;
616       for (Object loggerContext : loggerContexts) {
617         Map<String, Object> loggerConfigs = getLoggerConfigs(loggerContext);
618         loggerConfig = loggerConfigs.get(root ? "" : logName);
619         if (loggerConfig != null) {
620           break;
621         }
622       }
623       if (loggerConfig != null) {
624         Log4J2LoggerConfigAccessor accessor = new Log4J2LoggerConfigAccessor();
625         accessor.setTarget(loggerConfig);
626         accessor.setApplication(application);
627         accessor.setContext(true);
628         accessor.setLoggerContext(loggerContextAccessor);
629         result = accessor.getAppender(appenderName);
630       }
631     } catch (Exception e) {
632       logger.debug("getLog4J2LogDestination failed", e);
633     }
634     logger.debug("getLog4J2LogDestination(): OUT: result={}", result);
635 
636     return result;
637   }
638 
639   /**
640    * Gets the logger configs.
641    *
642    * @param loggerContext the logger context
643    *
644    * @return the logger configs
645    *
646    * @throws IllegalAccessException the illegal access exception
647    * @throws InvocationTargetException the invocation target exception
648    */
649   private Map<String, Object> getLoggerConfigs(Object loggerContext)
650       throws IllegalAccessException, InvocationTargetException {
651     Method getConfiguration =
652         MethodUtils.getAccessibleMethod(loggerContext.getClass(), "getConfiguration");
653     Object configuration = getConfiguration.invoke(loggerContext);
654     Method getLoggerConfigs =
655         MethodUtils.getAccessibleMethod(configuration.getClass(), "getLoggers");
656     return (Map<String, Object>) getLoggerConfigs.invoke(configuration);
657   }
658 
659   /**
660    * Gets the logger contexts.
661    *
662    * @param cl the cl
663    *
664    * @return the logger contexts
665    *
666    * @throws ClassNotFoundException the class not found exception
667    * @throws InstantiationException the instantiation exception
668    * @throws IllegalAccessException the illegal access exception
669    * @throws InvocationTargetException the invocation target exception
670    * @throws IllegalArgumentException the illegal argument exception
671    * @throws NoSuchMethodException the no such method exception
672    * @throws SecurityException the security exception
673    */
674   private List<Object> getLoggerContexts(ClassLoader cl) throws ClassNotFoundException,
675       InstantiationException, IllegalAccessException, InvocationTargetException,
676       IllegalArgumentException, NoSuchMethodException, SecurityException {
677     Class<?> clazz =
678         cl.loadClass("org.apache.logging.log4j.core.selector.ClassLoaderContextSelector");
679     Object classLoaderContextSelector = clazz.getDeclaredConstructor().newInstance();
680     Method getLoggerContexts = MethodUtils.getAccessibleMethod(clazz, "getLoggerContexts");
681     return (List<Object>) getLoggerContexts.invoke(classLoaderContextSelector);
682   }
683 
684   /**
685    * Gets the logback log destination.
686    *
687    * @param cl the cl
688    * @param application the application
689    * @param root the root
690    * @param logName the log name
691    * @param appenderName the appender name
692    *
693    * @return the logback log destination
694    */
695   private LogDestination getLogbackLogDestination(ClassLoader cl, Application application,
696       boolean root, String logName, String appenderName) {
697 
698     try {
699       LogbackFactoryAccessor manager = new LogbackFactoryAccessor(cl);
700       manager.setApplication(application);
701       LogbackLoggerAccessor log = root ? manager.getRootLogger() : manager.getLogger(logName);
702       if (log != null) {
703         return log.getAppender(appenderName);
704       }
705     } catch (Exception e) {
706       logger.debug("getLogbackLogDestination failed", e);
707     }
708     return null;
709   }
710 
711   /**
712    * Gets the logback log destination.
713    *
714    * @param cl the cl
715    * @param application the application
716    * @param root the root
717    * @param logName the log name
718    * @param appenderName the appender name
719    *
720    * @return the logback log destination
721    */
722   private LogDestination getLogback13LogDestination(ClassLoader cl, Application application,
723       boolean root, String logName, String appenderName) {
724 
725     try {
726       Logback13FactoryAccessor manager = new Logback13FactoryAccessor(cl);
727       manager.setApplication(application);
728       Logback13LoggerAccessor log = root ? manager.getRootLogger() : manager.getLogger(logName);
729       if (log != null) {
730         return log.getAppender(appenderName);
731       }
732     } catch (Exception e) {
733       logger.debug("getLogback13LogDestination failed", e);
734     }
735     return null;
736   }
737 
738   /**
739    * Gets the logback tomcat juli log destination.
740    *
741    * @param cl the cl
742    * @param application the application
743    * @param root the root
744    * @param logName the log name
745    * @param appenderName the appender name
746    *
747    * @return the logback tomcat juli log destination
748    */
749   private LogDestination getLogbackTomcatJuliLogDestination(ClassLoader cl, Application application,
750       boolean root, String logName, String appenderName) {
751 
752     try {
753       TomcatSlf4jLogbackFactoryAccessor manager = new TomcatSlf4jLogbackFactoryAccessor(cl);
754       manager.setApplication(application);
755       TomcatSlf4jLogbackLoggerAccessor log =
756           root ? manager.getRootLogger() : manager.getLogger(logName);
757       if (log != null) {
758         return log.getAppender(appenderName);
759       }
760     } catch (Exception e) {
761       logger.debug("getTomcatSlf4jLogbackLogDestination failed", e);
762     }
763     return null;
764   }
765 
766   /**
767    * Gets the logback tomcat juli log destination.
768    *
769    * @param cl the cl
770    * @param application the application
771    * @param root the root
772    * @param logName the log name
773    * @param appenderName the appender name
774    *
775    * @return the logback tomcat juli log destination
776    */
777   private LogDestination getLogback13TomcatJuliLogDestination(ClassLoader cl,
778       Application application, boolean root, String logName, String appenderName) {
779 
780     try {
781       TomcatSlf4jLogback13FactoryAccessor manager = new TomcatSlf4jLogback13FactoryAccessor(cl);
782       manager.setApplication(application);
783       TomcatSlf4jLogback13LoggerAccessor log =
784           root ? manager.getRootLogger() : manager.getLogger(logName);
785       if (log != null) {
786         return log.getAppender(appenderName);
787       }
788     } catch (Exception e) {
789       logger.debug("getTomcatSlf4jLogback13LogDestination failed", e);
790     }
791     return null;
792   }
793 
794   /**
795    * The Class AbstractLogComparator.
796    */
797   private abstract static class AbstractLogComparator
798       implements Comparator<LogDestination>, Serializable {
799 
800     /** The Constant serialVersionUID. */
801     private static final long serialVersionUID = 1L;
802 
803     /** The Constant DELIM. */
804     protected static final char DELIM = '!';
805 
806     @Override
807     public final int compare(LogDestination o1, LogDestination o2) {
808       String name1 = convertToString(o1);
809       String name2 = convertToString(o2);
810       return name1.compareTo(name2);
811     }
812 
813     /**
814      * Convert to string.
815      *
816      * @param d1 the d1
817      *
818      * @return the string
819      */
820     protected abstract String convertToString(LogDestination d1);
821 
822   }
823 
824   /**
825    * The Class LogDestinationComparator.
826    */
827   private static class LogDestinationComparator extends AbstractLogComparator
828       implements Serializable {
829 
830     /** The Constant serialVersionUID. */
831     private static final long serialVersionUID = 1L;
832 
833     /** The all. */
834     private final boolean all;
835 
836     /**
837      * Instantiates a new log destination comparator.
838      *
839      * @param all the all
840      */
841     public LogDestinationComparator(boolean all) {
842       this.all = all;
843     }
844 
845     @Override
846     protected String convertToString(LogDestination dest) {
847       File file = dest.getFile();
848       String fileName = file == null ? "" : file.getAbsolutePath();
849       String name;
850       if (all) {
851         Application app = dest.getApplication();
852         String appName = app == null ? Character.toString(DELIM) : app.getName();
853         String context = dest.isContext() ? "is" : "not";
854         String root = dest.isRoot() ? "is" : "not";
855         String logType = dest.getLogType();
856         name = appName + DELIM + context + DELIM + root + DELIM + logType + DELIM + fileName;
857       } else {
858         name = fileName;
859       }
860       return name;
861     }
862 
863   }
864 
865   /**
866    * The Class LogSourceComparator.
867    */
868   private static class LogSourceComparator extends AbstractLogComparator implements Serializable {
869 
870     /** The Constant serialVersionUID. */
871     private static final long serialVersionUID = 1L;
872 
873     @Override
874     protected String convertToString(LogDestination dest) {
875       File file = dest.getFile();
876       String fileName = file == null ? "" : file.getAbsolutePath();
877       Application app = dest.getApplication();
878       String appName = app == null ? Character.toString(DELIM) : app.getName();
879       String logType = dest.getLogType();
880       String context = dest.isContext() ? "is" : "not";
881       String root = dest.isRoot() ? "is" : "not";
882       String logName = dest.getName();
883       return appName + DELIM + logType + DELIM + context + DELIM + root + DELIM + logName + DELIM
884           + fileName;
885     }
886 
887   }
888 
889 }