View Javadoc
1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.util;
15  
16  import ch.qos.logback.classic.ClassicConstants;
17  import ch.qos.logback.classic.LoggerContext;
18  import ch.qos.logback.classic.spi.Configurator;
19  import ch.qos.logback.classic.spi.ConfiguratorRank;
20  import ch.qos.logback.core.LogbackException;
21  import ch.qos.logback.core.joran.spi.JoranException;
22  import ch.qos.logback.core.spi.ContextAware;
23  import ch.qos.logback.core.spi.ContextAwareImpl;
24  import ch.qos.logback.core.util.CoreVersionUtil;
25  import ch.qos.logback.core.util.Loader;
26  import ch.qos.logback.core.util.StatusListenerConfigHelper;
27  import ch.qos.logback.core.util.VersionUtil;
28  
29  import java.util.Comparator;
30  import java.util.List;
31  
32  // contributors
33  // Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32
34  
35  /**
36   * This class contains logback's logic for automatic configuration
37   *
38   * @author Ceki Gulcu
39   */
40  public class ContextInitializer {
41  
42      /**
43       * @deprecated Please use ClassicConstants.AUTOCONFIG_FILE instead
44       */
45      final public static String AUTOCONFIG_FILE = ClassicConstants.AUTOCONFIG_FILE;
46      /**
47       * @deprecated Please use ClassicConstants.TEST_AUTOCONFIG_FILE instead
48       */
49      final public static String TEST_AUTOCONFIG_FILE = ClassicConstants.TEST_AUTOCONFIG_FILE;
50      /**
51       * @deprecated Please use ClassicConstants.CONFIG_FILE_PROPERTY instead
52       */
53      final public static String CONFIG_FILE_PROPERTY = ClassicConstants.CONFIG_FILE_PROPERTY;
54  
55      String[] INTERNAL_CONFIGURATOR_CLASSNAME_LIST = {"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          int authConfiguratorCount = authConfiguratorCount(configuratorList);
91          if (authConfiguratorCount > 1) {
92              contextAware.addError("Multiple custom configurators of rank AUTHENTICATING were discovered as a service. This is not allowed.");
93              contextAware.addError("Ensure that at most one AUTHENTICATING configurator exists on the classpath.");
94              return;
95          }
96  
97          // invoke custom configurators
98          for (Configurator c : configuratorList) {
99              if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
100                 return;
101         }
102 
103         // invoke internal configurators
104         for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) {
105             contextAware.addInfo("Trying to configure with " + configuratorClassName);
106             Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader);
107             if (c == null)
108                 continue;
109             if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY)
110                 return;
111         }
112     }
113 
114     private int authConfiguratorCount(List<Configurator> configuratorList) {
115         int count = 0;
116         for (Configurator c : configuratorList) {
117             if (c.getClass().getAnnotation(ConfiguratorRank.class) != null
118                     && c.getClass().getAnnotation(ConfiguratorRank.class).value() == ConfiguratorRank.AUTHENTICATING) {
119                 contextAware.addInfo("Configurator of rank AUTHENTICATING discovered: " + c.getClass().getName());
120                 count++;
121 
122             }
123         }
124         return count;
125 
126     }
127 
128     private void checkVersions() {
129         try {
130             String coreVersion = CoreVersionUtil.getCoreVersionBySelfDeclaredProperties();
131             String classicVersion = ClassicVersionUtil.getVersionBySelfDeclaredProperties();
132             VersionUtil.checkForVersionEquality(loggerContext, coreVersion, classicVersion, "logback-core", "logback-classic");
133         } catch (NoClassDefFoundError e) {
134             contextAware.addWarn("Missing ch.logback.core.util.VersionUtil class on classpath. The version of logback-core is probably older than 1.5.26.");
135         } catch (NoSuchMethodError e) {
136             contextAware.addWarn(e.toString());
137             contextAware.addWarn("The version of logback-core is probably older than 1.5.26.");
138         }
139     }
140 
141     private Configurator instantiateConfiguratorByClassName(String configuratorClassName, ClassLoader classLoader) {
142         try {
143             Class<?> classObj = classLoader.loadClass(configuratorClassName);
144             return (Configurator) classObj.getConstructor().newInstance();
145         } catch (ReflectiveOperationException e) {
146             contextAware.addInfo("Instantiation failure: " + e.toString());
147             return null;
148         }
149     }
150 
151     /**
152      *
153      * @param configurator
154      * @return ExecutionStatus
155      */
156     private Configurator.ExecutionStatus invokeConfigure(Configurator configurator) {
157         try {
158             long start = System.currentTimeMillis();
159             contextAware.addInfo("Constructed configurator of type " + configurator.getClass());
160             configurator.setContext(loggerContext);
161             Configurator.ExecutionStatus status = configurator.configure(loggerContext);
162             printDuration(start, configurator, status);
163             return status;
164 
165         } catch (Exception e) {
166             throw new LogbackException(String.format("Failed to initialize or to run Configurator: %s",
167                     configurator != null ? configurator.getClass().getCanonicalName() : "null"), e);
168         }
169     }
170 
171     private void printConfiguratorOrder(List<Configurator> configuratorList) {
172         contextAware.addInfo("Here is a list of configurators discovered as a service, by rank: ");
173         for (Configurator c : configuratorList) {
174             contextAware.addInfo("  " + c.getClass().getName());
175         }
176         contextAware.addInfo("They will be invoked in order until ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY is returned.");
177     }
178 
179     private void printDuration(long start, Configurator configurator, Configurator.ExecutionStatus executionStatus) {
180         long end = System.currentTimeMillis();
181         long diff = end - start;
182         contextAware.addInfo(configurator.getClass().getName() + ".configure() call lasted " + diff + " milliseconds. ExecutionStatus=" + executionStatus);
183     }
184 
185     private Configurator.ExecutionStatus attemptConfigurationUsingJoranUsingReflexion(ClassLoader classLoader) {
186 
187         try {
188             Class<?> djcClass = classLoader.loadClass("ch.qos.logback.classic.util.DefaultJoranConfigurator");
189             Configurator c = (Configurator) djcClass.newInstance();
190             c.setContext(loggerContext);
191             return c.configure(loggerContext);
192         } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
193             contextAware.addError("unexpected exception while instantiating DefaultJoranConfigurator", e);
194             return Configurator.ExecutionStatus.INVOKE_NEXT_IF_ANY;
195         }
196 
197     }
198 
199     Comparator<Configurator> rankComparator = new Comparator<Configurator>() {
200         @Override
201         public int compare(Configurator c1, Configurator c2) {
202 
203             ConfiguratorRank r1 = c1.getClass().getAnnotation(ConfiguratorRank.class);
204             ConfiguratorRank r2 = c2.getClass().getAnnotation(ConfiguratorRank.class);
205 
206             int value1 = r1 == null ? ConfiguratorRank.DEFAULT : r1.value();
207             int value2 = r2 == null ? ConfiguratorRank.DEFAULT : r2.value();
208 
209             int result = compareRankValue(value1, value2);
210             // reverse the result for high to low sort
211             return (-result);
212         }
213     };
214 
215     private int compareRankValue(int value1, int value2) {
216         if (value1 > value2)
217             return 1;
218         else if (value1 == value2)
219             return 0;
220         else return -1;
221 
222     }
223 }