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