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