View Javadoc

1   /**
2    * Copyright (c) 2004-2011 QOS.ch
3    * All rights reserved.
4    *
5    * Permission is hereby granted, free  of charge, to any person obtaining
6    * a  copy  of this  software  and  associated  documentation files  (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   *
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
15   *
16   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18   * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   *
24   */
25  package org.slf4j;
26  
27  import java.io.IOException;
28  import java.net.URL;
29  import java.util.*;
30  
31  import org.slf4j.helpers.NOPLoggerFactory;
32  import org.slf4j.helpers.SubstituteLoggerFactory;
33  import org.slf4j.helpers.Util;
34  import org.slf4j.impl.StaticLoggerBinder;
35  
36  /**
37   * The <code>LoggerFactory</code> is a utility class producing Loggers for
38   * various logging APIs, most notably for log4j, logback and JDK 1.4 logging.
39   * Other implementations such as {@link org.slf4j.impl.NOPLogger NOPLogger} and
40   * {@link org.slf4j.impl.SimpleLogger SimpleLogger} are also supported.
41   * <p/>
42   * <p/>
43   * <code>LoggerFactory</code> is essentially a wrapper around an
44   * {@link ILoggerFactory} instance bound with <code>LoggerFactory</code> at
45   * compile time.
46   * <p/>
47   * <p/>
48   * Please note that all methods in <code>LoggerFactory</code> are static.
49   *
50   * @author Ceki G&uuml;lc&uuml;
51   * @author Robert Elliot
52   */
53  public final class LoggerFactory {
54  
55    static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
56  
57    static final String NO_STATICLOGGERBINDER_URL = CODES_PREFIX + "#StaticLoggerBinder";
58    static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings";
59    static final String NULL_LF_URL = CODES_PREFIX + "#null_LF";
60    static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch";
61    static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger";
62  
63    static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
64    static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also "
65            + UNSUCCESSFUL_INIT_URL;
66  
67    static final int UNINITIALIZED = 0;
68    static final int ONGOING_INITIALIZATION = 1;
69    static final int FAILED_INITIALIZATION = 2;
70    static final int SUCCESSFUL_INITIALIZATION = 3;
71    static final int NOP_FALLBACK_INITIALIZATION = 4;
72  
73    static int INITIALIZATION_STATE = UNINITIALIZED;
74    static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
75    static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
76  
77    /**
78     * It is LoggerFactory's responsibility to track version changes and manage
79     * the compatibility list.
80     * <p/>
81     * <p/>
82     * It is assumed that all versions in the 1.6 are mutually compatible.
83     */
84    static private final String[] API_COMPATIBILITY_LIST = new String[]{"1.6"};
85  
86    // private constructor prevents instantiation
87    private LoggerFactory() {
88    }
89  
90    /**
91     * Force LoggerFactory to consider itself uninitialized.
92     * <p/>
93     * <p/>
94     * This method is intended to be called by classes (in the same package) for
95     * testing purposes. This method is internal. It can be modified, renamed or
96     * removed at any time without notice.
97     * <p/>
98     * <p/>
99     * You are strongly discouraged from calling this method in production code.
100    */
101   static void reset() {
102     INITIALIZATION_STATE = UNINITIALIZED;
103     TEMP_FACTORY = new SubstituteLoggerFactory();
104   }
105 
106   private final static void performInitialization() {
107     bind();
108     if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
109       versionSanityCheck();
110     }
111   }
112 
113   private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) {
114     if (msg == null)
115       return false;
116     if (msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1)
117       return true;
118     if (msg.indexOf("org.slf4j.impl.StaticLoggerBinder") != -1)
119       return true;
120     return false;
121   }
122 
123   private final static void bind() {
124     try {
125       Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
126       reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
127       // the next line does the binding
128       StaticLoggerBinder.getSingleton();
129       INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
130       reportActualBinding(staticLoggerBinderPathSet);
131       emitSubstituteLoggerWarning();
132     } catch (NoClassDefFoundError ncde) {
133       String msg = ncde.getMessage();
134       if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
135         INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
136         Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
137         Util.report("Defaulting to no-operation (NOP) logger implementation");
138         Util.report("See " + NO_STATICLOGGERBINDER_URL
139                 + " for further details.");
140       } else {
141         failedBinding(ncde);
142         throw ncde;
143       }
144     } catch (java.lang.NoSuchMethodError nsme) {
145       String msg = nsme.getMessage();
146       if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
147         INITIALIZATION_STATE = FAILED_INITIALIZATION;
148         Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
149         Util.report("Your binding is version 1.5.5 or earlier.");
150         Util.report("Upgrade your binding to version 1.6.x.");
151       }
152       throw nsme;
153     } catch (Exception e) {
154       failedBinding(e);
155       throw new IllegalStateException("Unexpected initialization failure", e);
156     }
157   }
158 
159   static void failedBinding(Throwable t) {
160     INITIALIZATION_STATE = FAILED_INITIALIZATION;
161     Util.report("Failed to instantiate SLF4J LoggerFactory", t);
162   }
163 
164   private final static void emitSubstituteLoggerWarning() {
165     List loggerNameList = TEMP_FACTORY.getLoggerNameList();
166     if (loggerNameList.size() == 0) {
167       return;
168     }
169     Util.report("The following loggers will not work because they were created");
170     Util.report("during the default configuration phase of the underlying logging system.");
171     Util.report("See also " + SUBSTITUTE_LOGGER_URL);
172     for (int i = 0; i < loggerNameList.size(); i++) {
173       String loggerName = (String) loggerNameList.get(i);
174       Util.report(loggerName);
175     }
176   }
177 
178   private final static void versionSanityCheck() {
179     try {
180       String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
181 
182       boolean match = false;
183       for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
184         if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
185           match = true;
186         }
187       }
188       if (!match) {
189         Util.report("The requested version " + requested
190                 + " by your slf4j binding is not compatible with "
191                 + Arrays.asList(API_COMPATIBILITY_LIST).toString());
192         Util.report("See " + VERSION_MISMATCH + " for further details.");
193       }
194     } catch (java.lang.NoSuchFieldError nsfe) {
195       // given our large user base and SLF4J's commitment to backward
196       // compatibility, we cannot cry here. Only for implementations
197       // which willingly declare a REQUESTED_API_VERSION field do we
198       // emit compatibility warnings.
199     } catch (Throwable e) {
200       // we should never reach here
201       Util.report("Unexpected problem occured during version sanity check", e);
202     }
203   }
204 
205   // We need to use the name of the StaticLoggerBinder class, but we can't reference
206   // the class itself.
207   private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
208 
209   private static Set findPossibleStaticLoggerBinderPathSet() {
210     // use Set instead of list in order to deal with  bug #138
211     // LinkedHashSet appropriate here because it preserves insertion order during iteration
212     Set staticLoggerBinderPathSet = new LinkedHashSet();
213     try {
214       ClassLoader loggerFactoryClassLoader = LoggerFactory.class
215               .getClassLoader();
216       Enumeration paths;
217       if (loggerFactoryClassLoader == null) {
218         paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
219       } else {
220         paths = loggerFactoryClassLoader
221                 .getResources(STATIC_LOGGER_BINDER_PATH);
222       }
223       while (paths.hasMoreElements()) {
224         URL path = (URL) paths.nextElement();
225         staticLoggerBinderPathSet.add(path);
226       }
227     } catch (IOException ioe) {
228       Util.report("Error getting resources from path", ioe);
229     }
230     return staticLoggerBinderPathSet;
231   }
232 
233   private static boolean isAmbiguousStaticLoggerBinderPathSet(Set staticLoggerBinderPathSet) {
234     return staticLoggerBinderPathSet.size() > 1;
235   }
236 
237   /**
238    * Prints a warning message on the console if multiple bindings were found on the class path.
239    * No reporting is done otherwise.
240    *
241    */
242   private static void reportMultipleBindingAmbiguity(Set staticLoggerBinderPathSet) {
243     if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
244       Util.report("Class path contains multiple SLF4J bindings.");
245       Iterator iterator = staticLoggerBinderPathSet.iterator();
246       while (iterator.hasNext()) {
247         URL path = (URL) iterator.next();
248         Util.report("Found binding in [" + path + "]");
249       }
250       Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
251     }
252   }
253 
254   private static void reportActualBinding(Set staticLoggerBinderPathSet) {
255     if (isAmbiguousStaticLoggerBinderPathSet(staticLoggerBinderPathSet)) {
256       Util.report("Actual binding is of type ["+StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr()+"]");
257     }
258   }
259 
260 
261   /**
262    * Return a logger named according to the name parameter using the statically
263    * bound {@link ILoggerFactory} instance.
264    *
265    * @param name The name of the logger.
266    * @return logger
267    */
268   public static Logger getLogger(String name) {
269     ILoggerFactory iLoggerFactory = getILoggerFactory();
270     return iLoggerFactory.getLogger(name);
271   }
272 
273   /**
274    * Return a logger named corresponding to the class passed as parameter, using
275    * the statically bound {@link ILoggerFactory} instance.
276    *
277    * @param clazz the returned logger will be named after clazz
278    * @return logger
279    */
280   public static Logger getLogger(Class clazz) {
281     return getLogger(clazz.getName());
282   }
283 
284   /**
285    * Return the {@link ILoggerFactory} instance in use.
286    * <p/>
287    * <p/>
288    * ILoggerFactory instance is bound with this class at compile time.
289    *
290    * @return the ILoggerFactory instance in use
291    */
292   public static ILoggerFactory getILoggerFactory() {
293     if (INITIALIZATION_STATE == UNINITIALIZED) {
294       INITIALIZATION_STATE = ONGOING_INITIALIZATION;
295       performInitialization();
296     }
297     switch (INITIALIZATION_STATE) {
298       case SUCCESSFUL_INITIALIZATION:
299         return StaticLoggerBinder.getSingleton().getLoggerFactory();
300       case NOP_FALLBACK_INITIALIZATION:
301         return NOP_FALLBACK_FACTORY;
302       case FAILED_INITIALIZATION:
303         throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
304       case ONGOING_INITIALIZATION:
305         // support re-entrant behavior.
306         // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
307         return TEMP_FACTORY;
308     }
309     throw new IllegalStateException("Unreachable code");
310   }
311 }