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