View Javadoc

1   /*
2    * Copyright (c) 2004-2008 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.ArrayList;
30  import java.util.Arrays;
31  import java.util.Enumeration;
32  import java.util.List;
33  
34  import org.slf4j.helpers.SubstituteLoggerFactory;
35  import org.slf4j.helpers.Util;
36  import org.slf4j.impl.StaticLoggerBinder;
37  
38  /**
39   * The <code>LoggerFactory</code> is a utility class producing Loggers for
40   * various logging APIs, most notably for log4j, logback and JDK 1.4 logging.
41   * Other implementations such as {@link org.slf4j.impl.NOPLogger NOPLogger} and
42   * {@link org.slf4j.impl.SimpleLogger SimpleLogger} are also supported.
43   * 
44   * <p>
45   * <code>LoggerFactory</code> is essentially a wrapper around an
46   * {@link ILoggerFactory} instance bound with <code>LoggerFactory</code> at
47   * compile time.
48   * 
49   * <p>
50   * Please note that all methods in <code>LoggerFactory</code> are static.
51   * 
52   * @author Ceki G&uuml;lc&uuml;
53   * @author Robert Elliot
54   */
55  public final class LoggerFactory {
56  
57    static final String NO_STATICLOGGERBINDER_URL = "http://www.slf4j.org/codes.html#StaticLoggerBinder";
58    static final String MULTIPLE_BINDINGS_URL = "http://www.slf4j.org/codes.html#multiple_bindings";
59    static final String NULL_LF_URL = "http://www.slf4j.org/codes.html#null_LF";
60    static final String VERSION_MISMATCH = "http://www.slf4j.org/codes.html#version_mismatch";
61    static final String SUBSTITUTE_LOGGER_URL = "http://www.slf4j.org/codes.html#substituteLogger";
62  
63    static final String UNSUCCESSFUL_INIT_URL = "http://www.slf4j.org/codes.html#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_INITILIZATION = 1;
69    static final int FAILED_INITILIZATION = 2;
70    static final int SUCCESSFUL_INITILIZATION = 3;
71  
72    static final int GET_SINGLETON_INEXISTENT = 1;
73    static final int GET_SINGLETON_EXISTS = 2;
74  
75    static int INITIALIZATION_STATE = UNINITIALIZED;
76    static int GET_SINGLETON_METHOD = UNINITIALIZED;
77    static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
78  
79    /**
80     * It is LoggerFactory's responsibility to track version changes and manage
81     * the compatibility list.
82     * 
83     * <p>
84     * It is assumed that qualifiers after the 3rd digit have no impact on
85     * compatibility. Thus, 1.5.7-SNAPSHOT, 1.5.7.RC0 are compatible with 1.5.7.
86     */
87    static private final String[] API_COMPATIBILITY_LIST = new String[] {
88        "1.5.5", "1.5.6", "1.5.7", "1.5.8", "1.5.9", "1.5.10" };
89  
90    // private constructor prevents instantiation
91    private LoggerFactory() {
92    }
93  
94    /**
95     * Force LoggerFactory to consider itself uninitialized.
96     * 
97     * <p>
98     * This method is intended to be called by classes (in the same package) for
99     * testing purposes. This method is internal. It can be modified, renamed or
100    * removed at any time without notice.
101    * 
102    * <p>
103    * You are strongly discouraged from calling this method in production code.
104    */
105   static void reset() {
106     INITIALIZATION_STATE = UNINITIALIZED;
107     GET_SINGLETON_METHOD = UNINITIALIZED;
108     TEMP_FACTORY = new SubstituteLoggerFactory();
109   }
110 
111   private final static void performInitialization() {
112     bind();
113     versionSanityCheck();
114     singleImplementationSanityCheck();
115 
116   }
117 
118   private final static void bind() {
119     try {
120       // the next line does the binding
121       getSingleton();
122       INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
123       emitSubstituteLoggerWarning();
124     } catch (NoClassDefFoundError ncde) {
125       INITIALIZATION_STATE = FAILED_INITILIZATION;
126       String msg = ncde.getMessage();
127       if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
128         Util
129             .reportFailure("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
130         Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL
131             + " for further details.");
132 
133       }
134       throw ncde;
135     } catch (Exception e) {
136       INITIALIZATION_STATE = FAILED_INITILIZATION;
137       // we should never get here
138       Util.reportFailure("Failed to instantiate logger ["
139           + getSingleton().getLoggerFactoryClassStr() + "]", e);
140     }
141   }
142 
143   private final static void emitSubstituteLoggerWarning() {
144     List loggerNameList = TEMP_FACTORY.getLoggerNameList();
145     if (loggerNameList.size() == 0) {
146       return;
147     }
148     Util
149         .reportFailure("The following loggers will not work becasue they were created");
150     Util
151         .reportFailure("during the default configuration phase of the underlying logging system.");
152     Util.reportFailure("See also " + SUBSTITUTE_LOGGER_URL);
153     for (int i = 0; i < loggerNameList.size(); i++) {
154       String loggerName = (String) loggerNameList.get(i);
155       Util.reportFailure(loggerName);
156     }
157   }
158 
159   private final static void versionSanityCheck() {
160     try {
161       String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
162 
163       boolean match = false;
164       for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
165         if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
166           match = true;
167         }
168       }
169       if (!match) {
170         Util.reportFailure("The requested version " + requested
171             + " by your slf4j binding is not compatible with "
172             + Arrays.asList(API_COMPATIBILITY_LIST).toString());
173         Util.reportFailure("See " + VERSION_MISMATCH + " for further details.");
174       }
175     } catch (java.lang.NoSuchFieldError nsfe) {
176       // given our large user base and SLF4J's commitment to backward
177       // compatibility, we cannot cry here. Only for implementations
178       // which willingly declare a REQUESTED_API_VERSION field do we
179       // emit compatibility warnings.
180     } catch (Throwable e) {
181       // we should never reach here
182       Util.reportFailure(
183           "Unexpected problem occured during version sanity check", e);
184     }
185   }
186 
187   // We need to use the name of the StaticLoggerBinder class, we can't reference
188   // the class itseld.
189   private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
190 
191   private static void singleImplementationSanityCheck() {
192     try {
193       ClassLoader loggerFactoryClassLoader = LoggerFactory.class
194           .getClassLoader();
195       if (loggerFactoryClassLoader == null) {
196         // see http://bugzilla.slf4j.org/show_bug.cgi?id=146
197         return; // better than a null pointer exception
198       }
199       Enumeration paths = loggerFactoryClassLoader
200           .getResources(STATIC_LOGGER_BINDER_PATH);
201       List implementationList = new ArrayList();
202       while (paths.hasMoreElements()) {
203         URL path = (URL) paths.nextElement();
204         implementationList.add(path);
205       }
206       if (implementationList.size() > 1) {
207         Util.reportFailure("Class path contains multiple SLF4J bindings.");
208         for (int i = 0; i < implementationList.size(); i++) {
209           Util.reportFailure("Found binding in [" + implementationList.get(i)
210               + "]");
211         }
212         Util.reportFailure("See " + MULTIPLE_BINDINGS_URL
213             + " for an explanation.");
214       }
215     } catch (IOException ioe) {
216       Util.reportFailure("Error getting resources from path", ioe);
217     }
218   }
219 
220   private final static StaticLoggerBinder getSingleton() {
221     if (GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {
222       return StaticLoggerBinder.SINGLETON;
223     }
224 
225     if (GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {
226       return StaticLoggerBinder.getSingleton();
227     }
228 
229     try {
230       StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();
231       GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;
232       return singleton;
233     } catch (NoSuchMethodError nsme) {
234       GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;
235       return StaticLoggerBinder.SINGLETON;
236     }
237   }
238 
239   /**
240    * Return a logger named according to the name parameter using the statically
241    * bound {@link ILoggerFactory} instance.
242    * 
243    * @param name
244    *          The name of the logger.
245    * @return logger
246    */
247   public static Logger getLogger(String name) {
248     ILoggerFactory iLoggerFactory = getILoggerFactory();
249     return iLoggerFactory.getLogger(name);
250   }
251 
252   /**
253    * Return a logger named corresponding to the class passed as parameter, using
254    * the statically bound {@link ILoggerFactory} instance.
255    * 
256    * @param clazz
257    *          the returned logger will be named after clazz
258    * @return logger
259    */
260   public static Logger getLogger(Class clazz) {
261     return getLogger(clazz.getName());
262   }
263 
264   /**
265    * Return the {@link ILoggerFactory} instance in use.
266    * 
267    * <p>
268    * ILoggerFactory instance is bound with this class at compile time.
269    * 
270    * @return the ILoggerFactory instance in use
271    */
272   public static ILoggerFactory getILoggerFactory() {
273     if (INITIALIZATION_STATE == UNINITIALIZED) {
274       INITIALIZATION_STATE = ONGOING_INITILIZATION;
275       performInitialization();
276 
277     }
278     switch (INITIALIZATION_STATE) {
279     case SUCCESSFUL_INITILIZATION:
280       return getSingleton().getLoggerFactory();
281     case FAILED_INITILIZATION:
282       throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
283     case ONGOING_INITILIZATION:
284       // support re-entrant behavior.
285       // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
286       return TEMP_FACTORY;
287     }
288     throw new IllegalStateException("Unreachable code");
289   }
290 }