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.File;
28  import java.io.IOException;
29  import java.net.URL;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.Enumeration;
33  import java.util.List;
34  
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 NO_STATICLOGGERBINDER_URL = "http://www.slf4j.org/codes.html#StaticLoggerBinder";
59    static final String MULTIPLE_BINDINGS_URL = "http://www.slf4j.org/codes.html#multiple_bindings";
60    static final String NULL_LF_URL = "http://www.slf4j.org/codes.html#null_LF";
61    static final String VERSION_MISMATCH = "http://www.slf4j.org/codes.html#version_mismatch";
62    static final String SUBSTITUTE_LOGGER_URL = "http://www.slf4j.org/codes.html#substituteLogger";
63  
64    static final String UNSUCCESSFUL_INIT_URL = "http://www.slf4j.org/codes.html#unsuccessfulInit";
65    static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also "
66        + UNSUCCESSFUL_INIT_URL;
67  
68    static final int UNINITIALIZED = 0;
69    static final int ONGOING_INITILIZATION = 1;
70    static final int FAILED_INITILIZATION = 2;
71    static final int SUCCESSFUL_INITILIZATION = 3;
72  
73    static final int GET_SINGLETON_INEXISTENT = 1;
74    static final int GET_SINGLETON_EXISTS = 2;
75  
76    static int INITIALIZATION_STATE = UNINITIALIZED;
77    static int GET_SINGLETON_METHOD = UNINITIALIZED;
78    static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
79  
80    /**
81     * It is our responsibility to track version changes and manage the
82     * compatibility list.
83     * 
84     * <p>
85     */
86    static private final String[] API_COMPATIBILITY_LIST = new String[] {
87        "1.5.5", "1.5.6", "1.5.7", "1.5.8" };
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     GET_SINGLETON_METHOD = UNINITIALIZED;
107     TEMP_FACTORY = new SubstituteLoggerFactory();
108   }
109 
110   private final static void performInitialization() {
111     bind();
112     versionSanityCheck();
113     singleImplementationSanityCheck();
114 
115   }
116 
117   private final static void bind() {
118     try {
119       // the next line does the binding
120       getSingleton();
121       INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
122       emitSubstitureLoggerWarning();
123     } catch (NoClassDefFoundError ncde) {
124       INITIALIZATION_STATE = FAILED_INITILIZATION;
125       String msg = ncde.getMessage();
126       if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
127         Util
128             .reportFailure("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
129         Util.reportFailure("See " + NO_STATICLOGGERBINDER_URL
130             + " for further details.");
131 
132       }
133       throw ncde;
134     } catch (Exception e) {
135       INITIALIZATION_STATE = FAILED_INITILIZATION;
136       // we should never get here
137       Util.reportFailure("Failed to instantiate logger ["
138           + getSingleton().getLoggerFactoryClassStr() + "]", e);
139     }
140   }
141 
142   private final static void emitSubstitureLoggerWarning() {
143     List loggerNameList = TEMP_FACTORY.getLoggerNameList();
144     if (loggerNameList.size() == 0) {
145       return;
146     }
147     Util
148         .reportFailure("The following loggers will not work becasue they were created");
149     Util
150         .reportFailure("during the default configuration phase of the underlying logging system.");
151     Util.reportFailure("See also " + SUBSTITUTE_LOGGER_URL);
152     for (int i = 0; i < loggerNameList.size(); i++) {
153       String loggerName = (String) loggerNameList.get(i);
154       Util.reportFailure(loggerName);
155     }
156   }
157 
158   private final static void versionSanityCheck() {
159     try {
160       String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
161 
162       boolean match = false;
163       for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
164         if (API_COMPATIBILITY_LIST[i].equals(requested)) {
165           match = true;
166         }
167       }
168       if (!match) {
169         Util.reportFailure("The requested version " + requested
170             + " by your slf4j binding is not compatible with "
171             + Arrays.asList(API_COMPATIBILITY_LIST).toString());
172         Util.reportFailure("See " + VERSION_MISMATCH + " for further details.");
173       }
174     } catch (java.lang.NoSuchFieldError nsfe) {
175       // given our large user base and SLF4J's commitment to backward
176       // compatibility, we cannot cry here. Only for implementations
177       // which willingly declare a REQUESTED_API_VERSION field do we
178       // emit compatibility warnings.
179     } catch (Throwable e) {
180       // we should never reach here
181       Util.reportFailure(
182           "Unexpected problem occured during version sanity check", e);
183     }
184   }
185 
186   // We need to use the name of the StaticLoggerBinder class, we can't reference
187   // the class itseld.
188   private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
189 
190   private static void singleImplementationSanityCheck() {
191     try {
192       Enumeration paths = LoggerFactory.class.getClassLoader().getResources(
193           STATIC_LOGGER_BINDER_PATH);
194       List implementationList = new ArrayList();
195       while (paths.hasMoreElements()) {
196         URL path = (URL) paths.nextElement();
197         implementationList.add(path);
198       }
199       if (implementationList.size() > 1) {
200         Util
201             .reportFailure("Class path contains multiple SLF4J bindings.");
202         for(int i = 0; i < implementationList.size(); i++) {
203           Util.reportFailure("Found binding in ["+implementationList.get(i)+"]");
204         }
205         Util.reportFailure("See " + MULTIPLE_BINDINGS_URL
206             + " for an explanation.");
207       }
208     } catch (IOException ioe) {
209       Util.reportFailure("Error getting resources from path", ioe);
210     }
211   }
212 
213   private final static StaticLoggerBinder getSingleton() {
214     if (GET_SINGLETON_METHOD == GET_SINGLETON_INEXISTENT) {
215       return StaticLoggerBinder.SINGLETON;
216     }
217 
218     if (GET_SINGLETON_METHOD == GET_SINGLETON_EXISTS) {
219       return StaticLoggerBinder.getSingleton();
220     }
221 
222     try {
223       StaticLoggerBinder singleton = StaticLoggerBinder.getSingleton();
224       GET_SINGLETON_METHOD = GET_SINGLETON_EXISTS;
225       return singleton;
226     } catch (NoSuchMethodError nsme) {
227       GET_SINGLETON_METHOD = GET_SINGLETON_INEXISTENT;
228       return StaticLoggerBinder.SINGLETON;
229     }
230 
231   }
232 
233   /**
234    * Return a logger named according to the name parameter using the statically
235    * bound {@link ILoggerFactory} instance.
236    * 
237    * @param name
238    *                The name of the logger.
239    * @return logger
240    */
241   public static Logger getLogger(String name) {
242     ILoggerFactory iLoggerFactory = getILoggerFactory();
243     return iLoggerFactory.getLogger(name);
244   }
245 
246   /**
247    * Return a logger named corresponding to the class passed as parameter, using
248    * the statically bound {@link ILoggerFactory} instance.
249    * 
250    * @param clazz
251    *                the returned logger will be named after clazz
252    * @return logger
253    */
254   public static Logger getLogger(Class clazz) {
255     return getLogger(clazz.getName());
256   }
257 
258   /**
259    * Return the {@link ILoggerFactory} instance in use.
260    * 
261    * <p>
262    * ILoggerFactory instance is bound with this class at compile time.
263    * 
264    * @return the ILoggerFactory instance in use
265    */
266   public static ILoggerFactory getILoggerFactory() {
267     if (INITIALIZATION_STATE == UNINITIALIZED) {
268       INITIALIZATION_STATE = ONGOING_INITILIZATION;
269       performInitialization();
270 
271     }
272     switch (INITIALIZATION_STATE) {
273     case SUCCESSFUL_INITILIZATION:
274       return getSingleton().getLoggerFactory();
275     case FAILED_INITILIZATION:
276       throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
277     case ONGOING_INITILIZATION:
278       // support re-entrant behavior.
279       // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
280       return TEMP_FACTORY;
281     }
282     throw new IllegalStateException("Unreachable code");
283   }
284 }