1 package com.github.valfirst.slf4jtest;
2
3 import static java.util.Optional.ofNullable;
4
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collection;
8 import java.util.Collections;
9 import java.util.EnumSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Optional;
13 import java.util.Set;
14 import java.util.concurrent.CopyOnWriteArrayList;
15 import org.slf4j.Logger;
16 import org.slf4j.Marker;
17 import org.slf4j.event.KeyValuePair;
18 import org.slf4j.event.Level;
19 import org.slf4j.helpers.FormattingTuple;
20 import org.slf4j.helpers.MessageFormatter;
21 import org.slf4j.spi.LoggingEventAware;
22 import org.slf4j.spi.LoggingEventBuilder;
23 import uk.org.lidalia.lang.ThreadLocal;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 @SuppressWarnings({"PMD.ExcessivePublicCount", "PMD.TooManyMethods"})
44 public class TestLogger implements Logger, LoggingEventAware {
45
46 private final String name;
47 private final TestLoggerFactory testLoggerFactory;
48 private final ThreadLocal<List<LoggingEvent>> loggingEvents = new ThreadLocal<>(ArrayList::new);
49
50 private final List<LoggingEvent> allLoggingEvents = new CopyOnWriteArrayList<>();
51
52 private static final Set<Level> allLevels =
53 Collections.unmodifiableSet(EnumSet.allOf(Level.class));
54 private volatile ThreadLocal<Set<Level>> enabledLevels = new ThreadLocal<>(allLevels);
55
56 TestLogger(final String name, final TestLoggerFactory testLoggerFactory) {
57 this.name = name;
58 this.testLoggerFactory = testLoggerFactory;
59 }
60
61 public String getName() {
62 return name;
63 }
64
65
66 @Override
67 public LoggingEventBuilder makeLoggingEventBuilder(Level level) {
68 return new TestLoggingEventBuilder(this, level);
69 }
70
71
72
73
74
75 public void clear() {
76 loggingEvents.get().clear();
77 enabledLevels.set(allLevels);
78 }
79
80
81
82
83
84 public void clearAll() {
85 allLoggingEvents.clear();
86 loggingEvents.reset();
87 enabledLevels.reset();
88 enabledLevels = new ThreadLocal<>(allLevels);
89 }
90
91
92
93
94
95
96 public List<LoggingEvent> getLoggingEvents() {
97 return Collections.unmodifiableList(new ArrayList<>(loggingEvents.get()));
98 }
99
100
101
102
103
104
105 public List<LoggingEvent> getAllLoggingEvents() {
106 return Collections.unmodifiableList(new ArrayList<>(allLoggingEvents));
107 }
108
109
110
111
112 @Override
113 public boolean isTraceEnabled() {
114 return enabledLevels.get().contains(Level.TRACE);
115 }
116
117 @Override
118 public void trace(final String message) {
119 log(Level.TRACE, message);
120 }
121
122 @Override
123 public void trace(final String format, final Object arg) {
124 log(Level.TRACE, format, arg);
125 }
126
127 @Override
128 public void trace(final String format, final Object arg1, final Object arg2) {
129 log(Level.TRACE, format, arg1, arg2);
130 }
131
132 @Override
133 public void trace(final String format, final Object... args) {
134 log(Level.TRACE, format, args);
135 }
136
137 @Override
138 public void trace(final String msg, final Throwable throwable) {
139 log(Level.TRACE, msg, throwable);
140 }
141
142 @Override
143 public boolean isTraceEnabled(final Marker marker) {
144 return enabledLevels.get().contains(Level.TRACE);
145 }
146
147 @Override
148 public void trace(final Marker marker, final String msg) {
149 log(Level.TRACE, marker, msg);
150 }
151
152 @Override
153 public void trace(final Marker marker, final String format, final Object arg) {
154 log(Level.TRACE, marker, format, arg);
155 }
156
157 @Override
158 public void trace(
159 final Marker marker, final String format, final Object arg1, final Object arg2) {
160 log(Level.TRACE, marker, format, arg1, arg2);
161 }
162
163 @Override
164 public void trace(final Marker marker, final String format, final Object... args) {
165 log(Level.TRACE, marker, format, args);
166 }
167
168 @Override
169 public void trace(final Marker marker, final String msg, final Throwable throwable) {
170 log(Level.TRACE, marker, msg, throwable);
171 }
172
173
174
175
176 @Override
177 public boolean isDebugEnabled() {
178 return enabledLevels.get().contains(Level.DEBUG);
179 }
180
181 @Override
182 public void debug(final String message) {
183 log(Level.DEBUG, message);
184 }
185
186 @Override
187 public void debug(final String format, final Object arg) {
188 log(Level.DEBUG, format, arg);
189 }
190
191 @Override
192 public void debug(final String format, final Object arg1, final Object arg2) {
193 log(Level.DEBUG, format, arg1, arg2);
194 }
195
196 @Override
197 public void debug(final String format, final Object... args) {
198 log(Level.DEBUG, format, args);
199 }
200
201 @Override
202 public void debug(final String msg, final Throwable throwable) {
203 log(Level.DEBUG, msg, throwable);
204 }
205
206 @Override
207 public boolean isDebugEnabled(final Marker marker) {
208 return enabledLevels.get().contains(Level.DEBUG);
209 }
210
211 @Override
212 public void debug(final Marker marker, final String msg) {
213 log(Level.DEBUG, marker, msg);
214 }
215
216 @Override
217 public void debug(final Marker marker, final String format, final Object arg) {
218 log(Level.DEBUG, marker, format, arg);
219 }
220
221 @Override
222 public void debug(
223 final Marker marker, final String format, final Object arg1, final Object arg2) {
224 log(Level.DEBUG, marker, format, arg1, arg2);
225 }
226
227 @Override
228 public void debug(final Marker marker, final String format, final Object... args) {
229 log(Level.DEBUG, marker, format, args);
230 }
231
232 @Override
233 public void debug(final Marker marker, final String msg, final Throwable throwable) {
234 log(Level.DEBUG, marker, msg, throwable);
235 }
236
237
238
239
240 @Override
241 public boolean isInfoEnabled() {
242 return enabledLevels.get().contains(Level.INFO);
243 }
244
245 @Override
246 public void info(final String message) {
247 log(Level.INFO, message);
248 }
249
250 @Override
251 public void info(final String format, final Object arg) {
252 log(Level.INFO, format, arg);
253 }
254
255 @Override
256 public void info(final String format, final Object arg1, final Object arg2) {
257 log(Level.INFO, format, arg1, arg2);
258 }
259
260 @Override
261 public void info(final String format, final Object... args) {
262 log(Level.INFO, format, args);
263 }
264
265 @Override
266 public void info(final String msg, final Throwable throwable) {
267 log(Level.INFO, msg, throwable);
268 }
269
270 @Override
271 public boolean isInfoEnabled(final Marker marker) {
272 return enabledLevels.get().contains(Level.INFO);
273 }
274
275 @Override
276 public void info(final Marker marker, final String msg) {
277 log(Level.INFO, marker, msg);
278 }
279
280 @Override
281 public void info(final Marker marker, final String format, final Object arg) {
282 log(Level.INFO, marker, format, arg);
283 }
284
285 @Override
286 public void info(final Marker marker, final String format, final Object arg1, final Object arg2) {
287 log(Level.INFO, marker, format, arg1, arg2);
288 }
289
290 @Override
291 public void info(final Marker marker, final String format, final Object... args) {
292 log(Level.INFO, marker, format, args);
293 }
294
295 @Override
296 public void info(final Marker marker, final String msg, final Throwable throwable) {
297 log(Level.INFO, marker, msg, throwable);
298 }
299
300
301
302
303 @Override
304 public boolean isWarnEnabled() {
305 return enabledLevels.get().contains(Level.WARN);
306 }
307
308 @Override
309 public void warn(final String message) {
310 log(Level.WARN, message);
311 }
312
313 @Override
314 public void warn(final String format, final Object arg) {
315 log(Level.WARN, format, arg);
316 }
317
318 @Override
319 public void warn(final String format, final Object arg1, final Object arg2) {
320 log(Level.WARN, format, arg1, arg2);
321 }
322
323 @Override
324 public void warn(final String format, final Object... args) {
325 log(Level.WARN, format, args);
326 }
327
328 @Override
329 public void warn(final String msg, final Throwable throwable) {
330 log(Level.WARN, msg, throwable);
331 }
332
333 @Override
334 public boolean isWarnEnabled(final Marker marker) {
335 return enabledLevels.get().contains(Level.WARN);
336 }
337
338 @Override
339 public void warn(final Marker marker, final String msg) {
340 log(Level.WARN, marker, msg);
341 }
342
343 @Override
344 public void warn(final Marker marker, final String format, final Object arg) {
345 log(Level.WARN, marker, format, arg);
346 }
347
348 @Override
349 public void warn(final Marker marker, final String format, final Object arg1, final Object arg2) {
350 log(Level.WARN, marker, format, arg1, arg2);
351 }
352
353 @Override
354 public void warn(final Marker marker, final String format, final Object... args) {
355 log(Level.WARN, marker, format, args);
356 }
357
358 @Override
359 public void warn(final Marker marker, final String msg, final Throwable throwable) {
360 log(Level.WARN, marker, msg, throwable);
361 }
362
363
364
365
366 @Override
367 public boolean isErrorEnabled() {
368 return enabledLevels.get().contains(Level.ERROR);
369 }
370
371 @Override
372 public void error(final String message) {
373 log(Level.ERROR, message);
374 }
375
376 @Override
377 public void error(final String format, final Object arg) {
378 log(Level.ERROR, format, arg);
379 }
380
381 @Override
382 public void error(final String format, final Object arg1, final Object arg2) {
383 log(Level.ERROR, format, arg1, arg2);
384 }
385
386 @Override
387 public void error(final String format, final Object... args) {
388 log(Level.ERROR, format, args);
389 }
390
391 @Override
392 public void error(final String msg, final Throwable throwable) {
393 log(Level.ERROR, msg, throwable);
394 }
395
396 @Override
397 public boolean isErrorEnabled(final Marker marker) {
398 return enabledLevels.get().contains(Level.ERROR);
399 }
400
401 @Override
402 public void error(final Marker marker, final String msg) {
403 log(Level.ERROR, marker, msg);
404 }
405
406 @Override
407 public void error(final Marker marker, final String format, final Object arg) {
408 log(Level.ERROR, marker, format, arg);
409 }
410
411 @Override
412 public void error(
413 final Marker marker, final String format, final Object arg1, final Object arg2) {
414 log(Level.ERROR, marker, format, arg1, arg2);
415 }
416
417 @Override
418 public void error(final Marker marker, final String format, final Object... args) {
419 log(Level.ERROR, marker, format, args);
420 }
421
422 @Override
423 public void error(final Marker marker, final String msg, final Throwable throwable) {
424 log(Level.ERROR, marker, msg, throwable);
425 }
426
427 @Override
428 public void log(org.slf4j.event.LoggingEvent event) {
429
430 if (enabledByGlobalCaptureLevel(event.getLevel())) {
431 addLoggingEvent(LoggingEvent.fromSlf4jEvent(event, mdc()));
432 }
433 }
434
435 private void log(final Level level, final String format, final Object... args) {
436 log(level, format, Optional.empty(), args);
437 }
438
439 private void log(final Level level, final String msg, final Throwable throwable) {
440 addLoggingEvent(level, Optional.empty(), ofNullable(throwable), msg);
441 }
442
443 private void log(
444 final Level level, final Marker marker, final String format, final Object... args) {
445 log(level, format, ofNullable(marker), args);
446 }
447
448 private void log(
449 final Level level, final Marker marker, final String msg, final Throwable throwable) {
450 addLoggingEvent(level, ofNullable(marker), ofNullable(throwable), msg);
451 }
452
453 private void log(
454 final Level level, final String format, final Optional<Marker> marker, final Object[] args) {
455 final FormattingTuple formattedArgs = MessageFormatter.arrayFormat(format, args);
456 addLoggingEvent(
457 level,
458 marker,
459 ofNullable(formattedArgs.getThrowable()),
460 format,
461 formattedArgs.getArgArray());
462 }
463
464 private void addLoggingEvent(
465 final Level level,
466 final Optional<Marker> marker,
467 final Optional<Throwable> throwable,
468 final String format,
469 final Object... args) {
470 addLoggingEvent(
471 level,
472 marker.map(Collections::singletonList).orElseGet(Collections::emptyList),
473 Collections.emptyList(),
474 throwable,
475 format,
476 args);
477 }
478
479 private void addLoggingEvent(
480 final Level level,
481 final List<Marker> markers,
482 final List<KeyValuePair> keyValuePairs,
483 final Optional<Throwable> throwable,
484 final String format,
485 final Object... args) {
486 if (enabledLevels.get().contains(level) && enabledByGlobalCaptureLevel(level)) {
487 final LoggingEvent event =
488 new LoggingEvent(
489 Optional.of(this), level, mdc(), markers, keyValuePairs, throwable, format, args);
490 addLoggingEvent(event);
491 }
492 }
493
494 private void addLoggingEvent(final LoggingEvent event) {
495 allLoggingEvents.add(event);
496 loggingEvents.get().add(event);
497 testLoggerFactory.addLoggingEvent(event);
498 optionallyPrint(event);
499 }
500
501 private boolean enabledByGlobalCaptureLevel(Level level) {
502 Level captureLevel = testLoggerFactory.getCaptureLevel();
503 return captureLevel != null && captureLevel.compareTo(level) >= 0;
504 }
505
506 private Map<String, String> mdc() {
507 return TestMDCAdapter.getInstance().getContextMap();
508 }
509
510 private void optionallyPrint(final LoggingEvent event) {
511 Level printLevel = testLoggerFactory.getPrintLevel();
512 if (printLevel != null && printLevel.compareTo(event.getLevel()) >= 0) {
513 event.print();
514 }
515 }
516
517
518
519
520
521
522 public Set<Level> getEnabledLevels() {
523 return enabledLevels.get();
524 }
525
526
527
528
529
530
531
532
533
534 public void setEnabledLevels(final Collection<Level> enabledLevels) {
535 this.enabledLevels.set(setOfLevels(enabledLevels));
536 }
537
538
539
540
541
542
543
544
545
546 public void setEnabledLevels(final Level... enabledLevels) {
547 setEnabledLevels(Arrays.asList(enabledLevels));
548 }
549
550
551
552
553
554
555
556
557
558 public void setEnabledLevelsForAllThreads(final Collection<Level> enabledLevelsForAllThreads) {
559 this.enabledLevels = new ThreadLocal<>(setOfLevels(enabledLevelsForAllThreads));
560 }
561
562
563
564
565
566
567
568
569
570 public void setEnabledLevelsForAllThreads(final Level... enabledLevelsForAllThreads) {
571 setEnabledLevelsForAllThreads(Arrays.asList(enabledLevelsForAllThreads));
572 }
573
574 private Set<Level> setOfLevels(final Collection<Level> levels) {
575 return levels.isEmpty()
576 ? Collections.emptySet()
577 : Collections.unmodifiableSet(EnumSet.copyOf(levels));
578 }
579 }