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ülcü
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 }