1
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.LinkedHashSet;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.concurrent.LinkedBlockingQueue;
36
37 import org.slf4j.event.SubstituteLoggingEvent;
38 import org.slf4j.helpers.NOPLoggerFactory;
39 import org.slf4j.helpers.SubstituteLogger;
40 import org.slf4j.helpers.SubstituteLoggerFactory;
41 import org.slf4j.helpers.Util;
42 import org.slf4j.impl.StaticLoggerBinder;
43
44
64 public final class LoggerFactory {
65
66 static final String CODES_PREFIX = "http:;
67
68 static final String NO_STATICLOGGERBINDER_URL = CODES_PREFIX + "#StaticLoggerBinder";
69 static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings";
70 static final String NULL_LF_URL = CODES_PREFIX + "#null_LF";
71 static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch";
72 static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger";
73 static final String LOGGER_NAME_MISMATCH_URL = CODES_PREFIX + "#loggerNameMismatch";
74 static final String REPLAY_URL = CODES_PREFIX + "#replay";
75
76 static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
77 static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also " + UNSUCCESSFUL_INIT_URL;
78
79 static final int UNINITIALIZED = 0;
80 static final int ONGOING_INITIALIZATION = 1;
81 static final int FAILED_INITIALIZATION = 2;
82 static final int SUCCESSFUL_INITIALIZATION = 3;
83 static final int NOP_FALLBACK_INITIALIZATION = 4;
84
85 static volatile int INITIALIZATION_STATE = UNINITIALIZED;
86 static final SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory();
87 static final NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();
88
89
90 static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch";
91 static final String JAVA_VENDOR_PROPERTY = "java.vendor.url";
92
93 static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY);
94
95
102 static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" };
103
104
105 private LoggerFactory() {
106 }
107
108
119 static void reset() {
120 INITIALIZATION_STATE = UNINITIALIZED;
121 }
122
123 private final static void performInitialization() {
124 bind();
125 if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
126 versionSanityCheck();
127 }
128 }
129
130 private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) {
131 if (msg == null)
132 return false;
133 if (msg.contains("org/slf4j/impl/StaticLoggerBinder"))
134 return true;
135 if (msg.contains("org.slf4j.impl.StaticLoggerBinder"))
136 return true;
137 return false;
138 }
139
140 private final static void bind() {
141 try {
142 Set<URL> staticLoggerBinderPathSet = null;
143
144
145 if (!isAndroid()) {
146 staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
147 reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
148 }
149
150 StaticLoggerBinder.getSingleton();
151 INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
152 reportActualBinding(staticLoggerBinderPathSet);
153 } catch (NoClassDefFoundError ncde) {
154 String msg = ncde.getMessage();
155 if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
156 INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
157 Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
158 Util.report("Defaulting to no-operation (NOP) logger implementation");
159 Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
160 } else {
161 failedBinding(ncde);
162 throw ncde;
163 }
164 } catch (java.lang.NoSuchMethodError nsme) {
165 String msg = nsme.getMessage();
166 if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
167 INITIALIZATION_STATE = FAILED_INITIALIZATION;
168 Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
169 Util.report("Your binding is version 1.5.5 or earlier.");
170 Util.report("Upgrade your binding to version 1.6.x.");
171 }
172 throw nsme;
173 } catch (Exception e) {
174 failedBinding(e);
175 throw new IllegalStateException("Unexpected initialization failure", e);
176 } finally {
177 postBindCleanUp();
178 }
179 }
180
181 private static void postBindCleanUp() {
182 fixSubstituteLoggers();
183 replayEvents();
184
185 SUBST_FACTORY.clear();
186 }
187
188 private static void fixSubstituteLoggers() {
189 synchronized (SUBST_FACTORY) {
190 SUBST_FACTORY.postInitialization();
191 for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) {
192 Logger logger = getLogger(substLogger.getName());
193 substLogger.setDelegate(logger);
194 }
195 }
196
197 }
198
199 static void failedBinding(Throwable t) {
200 INITIALIZATION_STATE = FAILED_INITIALIZATION;
201 Util.report("Failed to instantiate SLF4J LoggerFactory", t);
202 }
203
204 private static void replayEvents() {
205 final LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_FACTORY.getEventQueue();
206 final int queueSize = queue.size();
207 int count = 0;
208 final int maxDrain = 128;
209 List<SubstituteLoggingEvent> eventList = new ArrayList<SubstituteLoggingEvent>(maxDrain);
210 while (true) {
211 int numDrained = queue.drainTo(eventList, maxDrain);
212 if (numDrained == 0)
213 break;
214 for (SubstituteLoggingEvent event : eventList) {
215 replaySingleEvent(event);
216 if (count++ == 0)
217 emitReplayOrSubstituionWarning(event, queueSize);
218 }
219 eventList.clear();
220 }
221 }
222
223 private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) {
224 if (event.getLogger().isDelegateEventAware()) {
225 emitReplayWarning(queueSize);
226 } else if (event.getLogger().isDelegateNOP()) {
227
228 } else {
229 emitSubstitutionWarning();
230 }
231 }
232
233 private static void replaySingleEvent(SubstituteLoggingEvent event) {
234 if (event == null)
235 return;
236
237 SubstituteLogger substLogger = event.getLogger();
238 String loggerName = substLogger.getName();
239 if (substLogger.isDelegateNull()) {
240 throw new IllegalStateException("Delegate logger cannot be null at this state.");
241 }
242
243 if (substLogger.isDelegateNOP()) {
244
245 } else if (substLogger.isDelegateEventAware()) {
246 substLogger.log(event);
247 } else {
248 Util.report(loggerName);
249 }
250 }
251
252 private static void emitSubstitutionWarning() {
253 Util.report("The following set of substitute loggers may have been accessed");
254 Util.report("during the initialization phase. Logging calls during this");
255 Util.report("phase were not honored. However, subsequent logging calls to these");
256 Util.report("loggers will work as normally expected.");
257 Util.report("See also " + SUBSTITUTE_LOGGER_URL);
258 }
259
260 private static void emitReplayWarning(int eventCount) {
261 Util.report("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are");
262 Util.report("now being replayed. These are subject to the filtering rules of the underlying logging system.");
263 Util.report("See also " + REPLAY_URL);
264 }
265
266 private final static void versionSanityCheck() {
267 try {
268 String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
269
270 boolean match = false;
271 for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
272 if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
273 match = true;
274 }
275 }
276 if (!match) {
277 Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
278 + Arrays.asList(API_COMPATIBILITY_LIST).toString());
279 Util.report("See " + VERSION_MISMATCH + " for further details.");
280 }
281 } catch (java.lang.NoSuchFieldError nsfe) {
282
283
284
285
286 } catch (Throwable e) {
287
288 Util.report("Unexpected problem occured during version sanity check", e);
289 }
290 }
291
292
293
294
295 private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
296
297 static Set<URL> findPossibleStaticLoggerBinderPathSet() {
298
299
300
301 Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
302 try {
303 ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
304 Enumeration<URL> paths;
305 if (loggerFactoryClassLoader == null) {
306 paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
307 } else {
308 paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
309 }
310 while (paths.hasMoreElements()) {
311 URL path = paths.nextElement();
312 staticLoggerBinderPathSet.add(path);
313 }
314 } catch (IOException ioe) {
315 Util.report("Error getting resources from path", ioe);
316 }
317 return staticLoggerBinderPathSet;
318 }
319
320 private static boolean isAmbiguousStaticLoggerBinderPathSet(Set<URL> binderPathSet) {
321 return binderPathSet.size() > 1;
322 }
323
324
329 private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
330 if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
331 Util.report("Class path contains multiple SLF4J bindings.");
332 for (URL path : binderPathSet) {
333 Util.report("Found binding in [" + path + "]");
334 }
335 Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
336 }
337 }
338
339 private static boolean isAndroid() {
340 String vendor = Util.safeGetSystemProperty(JAVA_VENDOR_PROPERTY);
341 if (vendor == null)
342 return false;
343 return vendor.toLowerCase().contains("android");
344 }
345
346 private static void reportActualBinding(Set<URL> binderPathSet) {
347
348 if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
349 Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
350 }
351 }
352
353
361 public static Logger getLogger(String name) {
362 ILoggerFactory iLoggerFactory = getILoggerFactory();
363 return iLoggerFactory.getLogger(name);
364 }
365
366
387 public static Logger getLogger(Class<?> clazz) {
388 Logger logger = getLogger(clazz.getName());
389 if (DETECT_LOGGER_NAME_MISMATCH) {
390 Class<?> autoComputedCallingClass = Util.getCallingClass();
391 if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
392 Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
393 autoComputedCallingClass.getName()));
394 Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
395 }
396 }
397 return logger;
398 }
399
400 private static boolean nonMatchingClasses(Class<?> clazz, Class<?> autoComputedCallingClass) {
401 return !autoComputedCallingClass.isAssignableFrom(clazz);
402 }
403
404
412 public static ILoggerFactory getILoggerFactory() {
413 if (INITIALIZATION_STATE == UNINITIALIZED) {
414 synchronized (LoggerFactory.class) {
415 if (INITIALIZATION_STATE == UNINITIALIZED) {
416 INITIALIZATION_STATE = ONGOING_INITIALIZATION;
417 performInitialization();
418 }
419 }
420 }
421 switch (INITIALIZATION_STATE) {
422 case SUCCESSFUL_INITIALIZATION:
423 return StaticLoggerBinder.getSingleton().getLoggerFactory();
424 case NOP_FALLBACK_INITIALIZATION:
425 return NOP_FALLBACK_FACTORY;
426 case FAILED_INITIALIZATION:
427 throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
428 case ONGOING_INITIALIZATION:
429
430
431 return SUBST_FACTORY;
432 }
433 throw new IllegalStateException("Unreachable code");
434 }
435 }
436