1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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.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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public final class LoggerFactory {
65
66 static final String CODES_PREFIX = "http://www.slf4j.org/codes.html";
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 could not be successfully initialized. 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 SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory();
87 static 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
96
97
98
99
100
101
102 static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" };
103
104
105 private LoggerFactory() {
106 }
107
108
109
110
111
112
113
114
115
116
117
118
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 fixSubstituteLoggers();
154 replayEvents();
155
156 SUBST_FACTORY.clear();
157 } catch (NoClassDefFoundError ncde) {
158 String msg = ncde.getMessage();
159 if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
160 INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
161 Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
162 Util.report("Defaulting to no-operation (NOP) logger implementation");
163 Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
164 } else {
165 failedBinding(ncde);
166 throw ncde;
167 }
168 } catch (java.lang.NoSuchMethodError nsme) {
169 String msg = nsme.getMessage();
170 if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
171 INITIALIZATION_STATE = FAILED_INITIALIZATION;
172 Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
173 Util.report("Your binding is version 1.5.5 or earlier.");
174 Util.report("Upgrade your binding to version 1.6.x.");
175 }
176 throw nsme;
177 } catch (Exception e) {
178 failedBinding(e);
179 throw new IllegalStateException("Unexpected initialization failure", e);
180 }
181 }
182
183 private static void fixSubstituteLoggers() {
184 synchronized (SUBST_FACTORY) {
185 SUBST_FACTORY.postInitialization();
186 for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) {
187 Logger logger = getLogger(substLogger.getName());
188 substLogger.setDelegate(logger);
189 }
190 }
191
192 }
193
194 static void failedBinding(Throwable t) {
195 INITIALIZATION_STATE = FAILED_INITIALIZATION;
196 Util.report("Failed to instantiate SLF4J LoggerFactory", t);
197 }
198
199 private static void replayEvents() {
200 final LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_FACTORY.getEventQueue();
201 final int queueSize = queue.size();
202 int count = 0;
203 final int maxDrain = 128;
204 List<SubstituteLoggingEvent> eventList = new ArrayList<SubstituteLoggingEvent>(maxDrain);
205 while (true) {
206 int numDrained = queue.drainTo(eventList, maxDrain);
207 if (numDrained == 0)
208 break;
209 for (SubstituteLoggingEvent event : eventList) {
210 replaySingleEvent(event);
211 if (count++ == 0)
212 emitReplayOrSubstituionWarning(event, queueSize);
213 }
214 eventList.clear();
215 }
216 }
217
218 private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) {
219 if (event.getLogger().isDelegateEventAware()) {
220 emitReplayWarning(queueSize);
221 } else if (event.getLogger().isDelegateNOP()) {
222
223 } else {
224 emitSubstitutionWarning();
225 }
226 }
227
228 private static void replaySingleEvent(SubstituteLoggingEvent event) {
229 if (event == null)
230 return;
231
232 SubstituteLogger substLogger = event.getLogger();
233 String loggerName = substLogger.getName();
234 if (substLogger.isDelegateNull()) {
235 throw new IllegalStateException("Delegate logger cannot be null at this state.");
236 }
237
238 if (substLogger.isDelegateNOP()) {
239
240 } else if (substLogger.isDelegateEventAware()) {
241 substLogger.log(event);
242 } else {
243 Util.report(loggerName);
244 }
245 }
246
247 private static void emitSubstitutionWarning() {
248 Util.report("The following set of substitute loggers may have been accessed");
249 Util.report("during the initialization phase. Logging calls during this");
250 Util.report("phase were not honored. However, subsequent logging calls to these");
251 Util.report("loggers will work as normally expected.");
252 Util.report("See also " + SUBSTITUTE_LOGGER_URL);
253 }
254
255 private static void emitReplayWarning(int eventCount) {
256 Util.report("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are");
257 Util.report("now being replayed. These are subject to the filtering rules of the underlying logging system.");
258 Util.report("See also " + REPLAY_URL);
259 }
260
261 private final static void versionSanityCheck() {
262 try {
263 String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
264
265 boolean match = false;
266 for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
267 if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
268 match = true;
269 }
270 }
271 if (!match) {
272 Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
273 + Arrays.asList(API_COMPATIBILITY_LIST).toString());
274 Util.report("See " + VERSION_MISMATCH + " for further details.");
275 }
276 } catch (java.lang.NoSuchFieldError nsfe) {
277
278
279
280
281 } catch (Throwable e) {
282
283 Util.report("Unexpected problem occured during version sanity check", e);
284 }
285 }
286
287
288
289
290 private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
291
292 static Set<URL> findPossibleStaticLoggerBinderPathSet() {
293
294
295
296 Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
297 try {
298 ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
299 Enumeration<URL> paths;
300 if (loggerFactoryClassLoader == null) {
301 paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
302 } else {
303 paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
304 }
305 while (paths.hasMoreElements()) {
306 URL path = paths.nextElement();
307 staticLoggerBinderPathSet.add(path);
308 }
309 } catch (IOException ioe) {
310 Util.report("Error getting resources from path", ioe);
311 }
312 return staticLoggerBinderPathSet;
313 }
314
315 private static boolean isAmbiguousStaticLoggerBinderPathSet(Set<URL> binderPathSet) {
316 return binderPathSet.size() > 1;
317 }
318
319
320
321
322
323
324 private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
325 if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
326 Util.report("Class path contains multiple SLF4J bindings.");
327 for (URL path : binderPathSet) {
328 Util.report("Found binding in [" + path + "]");
329 }
330 Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
331 }
332 }
333
334 private static boolean isAndroid() {
335 String vendor = Util.safeGetSystemProperty(JAVA_VENDOR_PROPERTY);
336 if (vendor == null)
337 return false;
338 return vendor.toLowerCase().contains("android");
339 }
340
341 private static void reportActualBinding(Set<URL> binderPathSet) {
342
343 if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
344 Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
345 }
346 }
347
348
349
350
351
352
353
354
355
356 public static Logger getLogger(String name) {
357 ILoggerFactory iLoggerFactory = getILoggerFactory();
358 return iLoggerFactory.getLogger(name);
359 }
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382 public static Logger getLogger(Class<?> clazz) {
383 Logger logger = getLogger(clazz.getName());
384 if (DETECT_LOGGER_NAME_MISMATCH) {
385 Class<?> autoComputedCallingClass = Util.getCallingClass();
386 if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
387 Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
388 autoComputedCallingClass.getName()));
389 Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
390 }
391 }
392 return logger;
393 }
394
395 private static boolean nonMatchingClasses(Class<?> clazz, Class<?> autoComputedCallingClass) {
396 return !autoComputedCallingClass.isAssignableFrom(clazz);
397 }
398
399
400
401
402
403
404
405
406
407 public static ILoggerFactory getILoggerFactory() {
408 if (INITIALIZATION_STATE == UNINITIALIZED) {
409 synchronized (LoggerFactory.class) {
410 if (INITIALIZATION_STATE == UNINITIALIZED) {
411 INITIALIZATION_STATE = ONGOING_INITIALIZATION;
412 performInitialization();
413 }
414 }
415 }
416 switch (INITIALIZATION_STATE) {
417 case SUCCESSFUL_INITIALIZATION:
418 return StaticLoggerBinder.getSingleton().getLoggerFactory();
419 case NOP_FALLBACK_INITIALIZATION:
420 return NOP_FALLBACK_FACTORY;
421 case FAILED_INITIALIZATION:
422 throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
423 case ONGOING_INITIALIZATION:
424
425
426 return SUBST_FACTORY;
427 }
428 throw new IllegalStateException("Unreachable code");
429 }
430 }