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