1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights reserved.
3    * <p>
4    * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse
5    * Foundation
6    * <p>
7    * or (per the licensee's choosing)
8    * <p>
9    * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.
10   */
11  package ch.qos.logback.classic.util;
12  
13  import ch.qos.logback.classic.ClassicConstants;
14  import ch.qos.logback.classic.LoggerContext;
15  import ch.qos.logback.classic.spi.Configurator;
16  import ch.qos.logback.classic.spi.ConfiguratorRank;
17  import ch.qos.logback.core.CoreConstants;
18  import ch.qos.logback.core.LogbackException;
19  import ch.qos.logback.core.joran.spi.JoranException;
20  import ch.qos.logback.core.spi.ContextAware;
21  import ch.qos.logback.core.spi.ContextAwareImpl;
22  import ch.qos.logback.core.status.InfoStatus;
23  import ch.qos.logback.core.status.WarnStatus;
24  import ch.qos.logback.core.util.EnvUtil;
25  import ch.qos.logback.core.util.Loader;
26  import ch.qos.logback.core.util.StatusListenerConfigHelper;
27  
28  import java.util.Comparator;
29  import java.util.List;
30  
31  // contributors
32  // Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32
33  
34  /**
35   * This class contains logback's logic for automatic configuration
36   *
37   * @author Ceki Gulcu
38   */
39  public class ContextInitializer {
40  
41      /**
42       *  @deprecated Please use ClassicConstants.AUTOCONFIG_FILE instead
43       */
44      final public static String AUTOCONFIG_FILE = ClassicConstants.AUTOCONFIG_FILE;
45      /**
46       * @deprecated Please use ClassicConstants.TEST_AUTOCONFIG_FILE instead
47       */
48      final public static String TEST_AUTOCONFIG_FILE = ClassicConstants.TEST_AUTOCONFIG_FILE;
49      /**
50       * @deprecated Please use ClassicConstants.CONFIG_FILE_PROPERTY instead
51       */
52      final public static String CONFIG_FILE_PROPERTY = ClassicConstants.CONFIG_FILE_PROPERTY;
53  
54      String[] INTERNAL_CONFIGURATOR_CLASSNAME_LIST = {"ch.qos.logback.classic.joran.SerializedModelConfigurator",
55              "ch.qos.logback.classic.util.DefaultJoranConfigurator", "ch.qos.logback.classic.BasicConfigurator"};
56  
57      final LoggerContext loggerContext;
58  
59      final ContextAware contextAware;
60  
61      public ContextInitializer(LoggerContext loggerContext) {
62          this.loggerContext = loggerContext;
63          this.contextAware = new ContextAwareImpl(loggerContext, this);
64      }
65  
66      public void autoConfig() throws JoranException {
67          autoConfig(Configurator.class.getClassLoader());
68      }
69  
70  
71      public void autoConfig(ClassLoader classLoader) throws JoranException {
72  
73          // see https://github.com/qos-ch/logback/issues/715
74          classLoader = Loader.systemClassloaderIfNull(classLoader);
75  
76          checkVersions();
77  
78          StatusListenerConfigHelper.installIfAsked(loggerContext);
79  
80  
81          // invoke custom configurators
82          List<Configurator> configuratorList = ClassicEnvUtil.loadFromServiceLoader(Configurator.class, classLoader);
83          configuratorList.sort(rankComparator);
84          if (configuratorList.isEmpty()) {
85              contextAware.addInfo("No custom configurators were discovered as a service.");
86          } else {
87              printConfiguratorOrder(configuratorList);
88          }
89  
90          for (Configurator c : configuratorList) {
91              if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
92                  return;
93          }
94  
95          // invoke internal configurators
96          for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
97              contextAware.addInfo("Trying to configure with "+configuratorClassName);
98              Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
99              if(c == null)
100                 continue;
101             if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
102                 return;
103         }
104     }
105 
106     private void checkVersions() {
107         String versionOfLogbackClassic = ClassicEnvUtil.getVersionOfLogbackClassic();
108         if (versionOfLogbackClassic == null) {
109             versionOfLogbackClassic = CoreConstants.NA;
110         }
111         String versionOfLogbackCore = EnvUtil.logbackVersion();
112         if (versionOfLogbackCore == null) {
113             versionOfLogbackCore = CoreConstants.NA;
114         }
115         loggerContext.getStatusManager().add(new InfoStatus(ClassicConstants.LOGBACK_CLASSIC_VERSION_MESSAGE + versionOfLogbackClassic, loggerContext));
116         if(!versionOfLogbackCore.equals(versionOfLogbackClassic)) {
117             loggerContext.getStatusManager().add(new InfoStatus(CoreConstants.LOGBACK_CORE_VERSION_MESSAGE + versionOfLogbackCore, loggerContext));
118             loggerContext.getStatusManager().add(new WarnStatus(ClassicConstants.LOGBACK_VERSIONS_MISMATCH, loggerContext));
119         }
120     }
121 
122     private Configurator instantiateConfiguratorByClassName(String configuratorClassName, ClassLoader classLoader) {
123         try {
124             Class<?> classObj = classLoader.loadClass(configuratorClassName);
125             return (Configurator) classObj.getConstructor().newInstance();
126         } catch (ReflectiveOperationException  e) {
127             contextAware.addInfo("Instantiation failure: " + e.toString());
128             return null;
129         }
130     }
131 
132     /**
133      *
134      * @param configurator
135      * @return ExecutionStatus
136      */
137     private Configurator.ExecutionStatus invokeConfigure(Configurator configurator) {
138         try {
139             long start = System.currentTimeMillis();
140             contextAware.addInfo("Constructed configurator of type " + configurator.getClass());
141             configurator.setContext(loggerContext);
142             Configurator.ExecutionStatus status = configurator.configure(loggerContext);
143             printDuration(start, configurator, status);
144             return status;
145 
146         } catch (Exception e) {
147             throw new LogbackException(String.format("Failed to initialize or to run Configurator: %s",
148                     configurator != null ? configurator.getClass().getCanonicalName() : "null"), e);
149         }
150     }
151 
152     private void printConfiguratorOrder(List<Configurator> configuratorList) {
153         contextAware.addInfo("Here is a list of configurators discovered as a service, by rank: ");
154         for(Configurator c: configuratorList) {
155             contextAware.addInfo("  "+c.getClass().getName());
156         }
157         contextAware.addInfo("They will be invoked in order until ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY is returned.");
158     }
159 
160     private void printDuration(long start, Configurator configurator, Configurator.ExecutionStatus executionStatus) {
161         long end = System.currentTimeMillis();
162         long diff = end - start;
163         contextAware.addInfo( configurator.getClass().getName()+".configure() call lasted "+diff + " milliseconds. ExecutionStatus="+executionStatus);
164     }
165 
166     private Configurator.ExecutionStatus attemptConfigurationUsingJoranUsingReflexion(ClassLoader classLoader) {
167 
168         try {
169             Class<?> djcClass = classLoader.loadClass("ch.qos.logback.classic.util.DefaultJoranConfigurator");
170             Configurator c = (Configurator) djcClass.newInstance();
171             c.setContext(loggerContext);
172             return c.configure(loggerContext);
173         } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
174             contextAware.addError("unexpected exception while instantiating DefaultJoranConfigurator", e);
175             return Configurator.ExecutionStatus.INVOKE_NEXT_IF_ANY;
176         }
177 
178     }
179 
180     Comparator<Configurator> rankComparator = new Comparator<Configurator>() {
181         @Override
182         public int compare(Configurator c1, Configurator c2) {
183 
184             ConfiguratorRank r1 = c1.getClass().getAnnotation(ConfiguratorRank.class);
185             ConfiguratorRank r2 = c2.getClass().getAnnotation(ConfiguratorRank.class);
186 
187             int value1 = r1 == null ? ConfiguratorRank.DEFAULT : r1.value();
188             int value2 = r2 == null ? ConfiguratorRank.DEFAULT : r2.value();
189 
190             int result = compareRankValue(value1, value2);
191             // reverse the result for high to low sort
192             return (-result);
193         }
194     };
195 
196     private int compareRankValue(int value1, int value2) {
197         if(value1 > value2)
198             return 1;
199         else if (value1 == value2)
200             return 0;
201         else return -1;
202 
203     }
204 }