1 package com.github.valfirst.slf4jtest;
2
3 import static java.util.Objects.requireNonNull;
4 import static java.util.Optional.empty;
5 import static java.util.Optional.ofNullable;
6
7 import java.io.PrintStream;
8 import java.time.Instant;
9 import java.time.format.DateTimeFormatter;
10 import java.time.format.DateTimeFormatterBuilder;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Objects;
17 import java.util.Optional;
18 import java.util.SortedMap;
19 import java.util.TreeMap;
20 import org.slf4j.Marker;
21 import org.slf4j.event.KeyValuePair;
22 import org.slf4j.event.Level;
23 import org.slf4j.helpers.MessageFormatter;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 @SuppressWarnings({"PMD.ExcessivePublicCount", "PMD.TooManyMethods"})
47 public class LoggingEvent {
48 private static final DateTimeFormatter ISO_FORMAT =
49 new DateTimeFormatterBuilder().appendInstant(3).toFormatter();
50 private static final Object[] emptyObjectArray = {};
51
52 private final Level level;
53 private final SortedMap<String, String> mdc;
54 private final List<Marker> markers;
55 private final List<KeyValuePair> keyValuePairs;
56 private final Optional<Throwable> throwable;
57 private final String message;
58 private final List<Object> arguments;
59
60 private final Optional<TestLogger> creatingLogger;
61 private final Instant timestamp = Instant.now();
62 private final String threadName = Thread.currentThread().getName();
63 private final ClassLoader threadContextClassLoader =
64 Thread.currentThread().getContextClassLoader();
65
66 public static LoggingEvent trace(final String message, final Object... arguments) {
67 return new LoggingEvent(Level.TRACE, message, arguments);
68 }
69
70 public static LoggingEvent trace(
71 final Throwable throwable, final String message, final Object... arguments) {
72 return new LoggingEvent(Level.TRACE, throwable, message, arguments);
73 }
74
75 public static LoggingEvent trace(
76 final Marker marker, final String message, final Object... arguments) {
77 return new LoggingEvent(Level.TRACE, marker, message, arguments);
78 }
79
80 public static LoggingEvent trace(
81 final Marker marker,
82 final Throwable throwable,
83 final String message,
84 final Object... arguments) {
85 return new LoggingEvent(Level.TRACE, marker, throwable, message, arguments);
86 }
87
88 public static LoggingEvent trace(
89 final Map<String, String> mdc, final String message, final Object... arguments) {
90 return new LoggingEvent(Level.TRACE, mdc, message, arguments);
91 }
92
93 public static LoggingEvent trace(
94 final Map<String, String> mdc,
95 final Throwable throwable,
96 final String message,
97 final Object... arguments) {
98 return new LoggingEvent(Level.TRACE, mdc, throwable, message, arguments);
99 }
100
101 public static LoggingEvent trace(
102 final Map<String, String> mdc,
103 final Marker marker,
104 final String message,
105 final Object... arguments) {
106 return new LoggingEvent(Level.TRACE, mdc, marker, message, arguments);
107 }
108
109 public static LoggingEvent trace(
110 final Map<String, String> mdc,
111 final Marker marker,
112 final Throwable throwable,
113 final String message,
114 final Object... arguments) {
115 return new LoggingEvent(Level.TRACE, mdc, marker, throwable, message, arguments);
116 }
117
118 public static LoggingEvent debug(final String message, final Object... arguments) {
119 return new LoggingEvent(Level.DEBUG, message, arguments);
120 }
121
122 public static LoggingEvent debug(
123 final Throwable throwable, final String message, final Object... arguments) {
124 return new LoggingEvent(Level.DEBUG, throwable, message, arguments);
125 }
126
127 public static LoggingEvent debug(
128 final Marker marker, final String message, final Object... arguments) {
129 return new LoggingEvent(Level.DEBUG, marker, message, arguments);
130 }
131
132 public static LoggingEvent debug(
133 final Marker marker,
134 final Throwable throwable,
135 final String message,
136 final Object... arguments) {
137 return new LoggingEvent(Level.DEBUG, marker, throwable, message, arguments);
138 }
139
140 public static LoggingEvent debug(
141 final Map<String, String> mdc, final String message, final Object... arguments) {
142 return new LoggingEvent(Level.DEBUG, mdc, message, arguments);
143 }
144
145 public static LoggingEvent debug(
146 final Map<String, String> mdc,
147 final Throwable throwable,
148 final String message,
149 final Object... arguments) {
150 return new LoggingEvent(Level.DEBUG, mdc, throwable, message, arguments);
151 }
152
153 public static LoggingEvent debug(
154 final Map<String, String> mdc,
155 final Marker marker,
156 final String message,
157 final Object... arguments) {
158 return new LoggingEvent(Level.DEBUG, mdc, marker, message, arguments);
159 }
160
161 public static LoggingEvent debug(
162 final Map<String, String> mdc,
163 final Marker marker,
164 final Throwable throwable,
165 final String message,
166 final Object... arguments) {
167 return new LoggingEvent(Level.DEBUG, mdc, marker, throwable, message, arguments);
168 }
169
170 public static LoggingEvent info(final String message, final Object... arguments) {
171 return new LoggingEvent(Level.INFO, message, arguments);
172 }
173
174 public static LoggingEvent info(
175 final Throwable throwable, final String message, final Object... arguments) {
176 return new LoggingEvent(Level.INFO, throwable, message, arguments);
177 }
178
179 public static LoggingEvent info(
180 final Marker marker, final String message, final Object... arguments) {
181 return new LoggingEvent(Level.INFO, marker, message, arguments);
182 }
183
184 public static LoggingEvent info(
185 final Marker marker,
186 final Throwable throwable,
187 final String message,
188 final Object... arguments) {
189 return new LoggingEvent(Level.INFO, marker, throwable, message, arguments);
190 }
191
192 public static LoggingEvent info(
193 final Map<String, String> mdc, final String message, final Object... arguments) {
194 return new LoggingEvent(Level.INFO, mdc, message, arguments);
195 }
196
197 public static LoggingEvent info(
198 final Map<String, String> mdc,
199 final Throwable throwable,
200 final String message,
201 final Object... arguments) {
202 return new LoggingEvent(Level.INFO, mdc, throwable, message, arguments);
203 }
204
205 public static LoggingEvent info(
206 final Map<String, String> mdc,
207 final Marker marker,
208 final String message,
209 final Object... arguments) {
210 return new LoggingEvent(Level.INFO, mdc, marker, message, arguments);
211 }
212
213 public static LoggingEvent info(
214 final Map<String, String> mdc,
215 final Marker marker,
216 final Throwable throwable,
217 final String message,
218 final Object... arguments) {
219 return new LoggingEvent(Level.INFO, mdc, marker, throwable, message, arguments);
220 }
221
222 public static LoggingEvent warn(final String message, final Object... arguments) {
223 return new LoggingEvent(Level.WARN, message, arguments);
224 }
225
226 public static LoggingEvent warn(
227 final Throwable throwable, final String message, final Object... arguments) {
228 return new LoggingEvent(Level.WARN, throwable, message, arguments);
229 }
230
231 public static LoggingEvent warn(
232 final Marker marker, final String message, final Object... arguments) {
233 return new LoggingEvent(Level.WARN, marker, message, arguments);
234 }
235
236 public static LoggingEvent warn(
237 final Marker marker,
238 final Throwable throwable,
239 final String message,
240 final Object... arguments) {
241 return new LoggingEvent(Level.WARN, marker, throwable, message, arguments);
242 }
243
244 public static LoggingEvent warn(
245 final Map<String, String> mdc, final String message, final Object... arguments) {
246 return new LoggingEvent(Level.WARN, mdc, message, arguments);
247 }
248
249 public static LoggingEvent warn(
250 final Map<String, String> mdc,
251 final Throwable throwable,
252 final String message,
253 final Object... arguments) {
254 return new LoggingEvent(Level.WARN, mdc, throwable, message, arguments);
255 }
256
257 public static LoggingEvent warn(
258 final Map<String, String> mdc,
259 final Marker marker,
260 final String message,
261 final Object... arguments) {
262 return new LoggingEvent(Level.WARN, mdc, marker, message, arguments);
263 }
264
265 public static LoggingEvent warn(
266 final Map<String, String> mdc,
267 final Marker marker,
268 final Throwable throwable,
269 final String message,
270 final Object... arguments) {
271 return new LoggingEvent(Level.WARN, mdc, marker, throwable, message, arguments);
272 }
273
274 public static LoggingEvent error(final String message, final Object... arguments) {
275 return new LoggingEvent(Level.ERROR, message, arguments);
276 }
277
278 public static LoggingEvent error(
279 final Throwable throwable, final String message, final Object... arguments) {
280 return new LoggingEvent(Level.ERROR, throwable, message, arguments);
281 }
282
283 public static LoggingEvent error(
284 final Marker marker, final String message, final Object... arguments) {
285 return new LoggingEvent(Level.ERROR, marker, message, arguments);
286 }
287
288 public static LoggingEvent error(
289 final Marker marker,
290 final Throwable throwable,
291 final String message,
292 final Object... arguments) {
293 return new LoggingEvent(Level.ERROR, marker, throwable, message, arguments);
294 }
295
296 public static LoggingEvent error(
297 final Map<String, String> mdc, final String message, final Object... arguments) {
298 return new LoggingEvent(Level.ERROR, mdc, message, arguments);
299 }
300
301 public static LoggingEvent error(
302 final Map<String, String> mdc,
303 final Throwable throwable,
304 final String message,
305 final Object... arguments) {
306 return new LoggingEvent(Level.ERROR, mdc, throwable, message, arguments);
307 }
308
309 public static LoggingEvent error(
310 final Map<String, String> mdc,
311 final Marker marker,
312 final String message,
313 final Object... arguments) {
314 return new LoggingEvent(Level.ERROR, mdc, marker, message, arguments);
315 }
316
317 public static LoggingEvent error(
318 final Map<String, String> mdc,
319 final Marker marker,
320 final Throwable throwable,
321 final String message,
322 final Object... arguments) {
323 return new LoggingEvent(Level.ERROR, mdc, marker, throwable, message, arguments);
324 }
325
326
327
328
329
330
331 public static LoggingEvent fromSlf4jEvent(org.slf4j.event.LoggingEvent event) {
332 return fromSlf4jEvent(event, Collections.emptyMap());
333 }
334
335
336
337
338
339
340 public static LoggingEvent fromSlf4jEvent(
341 org.slf4j.event.LoggingEvent event, Map<String, String> mdc) {
342 List<Marker> markers = event.getMarkers();
343 List<KeyValuePair> keyValuePairs = event.getKeyValuePairs();
344 Object[] arguments = event.getArgumentArray();
345 return new LoggingEvent(
346 empty(),
347 event.getLevel(),
348 mdc,
349 markers == null
350 ? Collections.emptyList()
351 : Collections.unmodifiableList(new ArrayList<>(markers)),
352 keyValuePairs == null
353 ? Collections.emptyList()
354 : Collections.unmodifiableList(new ArrayList<>(keyValuePairs)),
355 ofNullable(event.getThrowable()),
356 event.getMessage(),
357 arguments == null ? emptyObjectArray : arguments);
358 }
359
360 public LoggingEvent(final Level level, final String message, final Object... arguments) {
361 this(level, Collections.emptySortedMap(), empty(), empty(), message, arguments);
362 }
363
364 public LoggingEvent(
365 final Level level,
366 final Throwable throwable,
367 final String message,
368 final Object... arguments) {
369 this(level, Collections.emptySortedMap(), empty(), ofNullable(throwable), message, arguments);
370 }
371
372 public LoggingEvent(
373 final Level level, final Marker marker, final String message, final Object... arguments) {
374 this(level, Collections.emptySortedMap(), ofNullable(marker), empty(), message, arguments);
375 }
376
377 public LoggingEvent(
378 final Level level,
379 final Marker marker,
380 final Throwable throwable,
381 final String message,
382 final Object... arguments) {
383 this(
384 level,
385 Collections.emptySortedMap(),
386 ofNullable(marker),
387 ofNullable(throwable),
388 message,
389 arguments);
390 }
391
392 public LoggingEvent(
393 final Level level,
394 final Map<String, String> mdc,
395 final String message,
396 final Object... arguments) {
397 this(level, mdc, empty(), empty(), message, arguments);
398 }
399
400 public LoggingEvent(
401 final Level level,
402 final Map<String, String> mdc,
403 final Throwable throwable,
404 final String message,
405 final Object... arguments) {
406 this(level, mdc, empty(), ofNullable(throwable), message, arguments);
407 }
408
409 public LoggingEvent(
410 final Level level,
411 final Map<String, String> mdc,
412 final Marker marker,
413 final String message,
414 final Object... arguments) {
415 this(level, mdc, ofNullable(marker), empty(), message, arguments);
416 }
417
418 public LoggingEvent(
419 final Level level,
420 final Map<String, String> mdc,
421 final Marker marker,
422 final Throwable throwable,
423 final String message,
424 final Object... arguments) {
425 this(level, mdc, ofNullable(marker), ofNullable(throwable), message, arguments);
426 }
427
428 private LoggingEvent(
429 final Level level,
430 final Map<String, String> mdc,
431 final Optional<Marker> marker,
432 final Optional<Throwable> throwable,
433 final String message,
434 final Object... arguments) {
435 this(
436 empty(),
437 level,
438 mdc,
439 marker.map(Collections::singletonList).orElseGet(Collections::emptyList),
440 Collections.emptyList(),
441 throwable,
442 message,
443 arguments);
444 }
445
446 LoggingEvent(
447 final Optional<TestLogger> creatingLogger,
448 final Level level,
449 final Map<String, String> mdc,
450 final List<Marker> markers,
451 final List<KeyValuePair> keyValuePairs,
452 final Optional<Throwable> throwable,
453 final String message,
454 final Object... arguments) {
455 super();
456 this.creatingLogger = creatingLogger;
457 this.level = requireNonNull(level);
458 this.mdc =
459 requireNonNull(mdc).isEmpty()
460 ? Collections.emptySortedMap()
461 : Collections.unmodifiableSortedMap(new TreeMap<>(mdc));
462 this.markers = markers;
463 this.keyValuePairs = keyValuePairs;
464 this.throwable = requireNonNull(throwable);
465 this.message = message;
466 this.arguments =
467 arguments.length == 0
468 ? Collections.emptyList()
469 : Collections.unmodifiableList(new ArrayList<>(Arrays.asList(arguments)));
470 }
471
472 public Level getLevel() {
473 return level;
474 }
475
476
477
478
479
480
481
482
483 public SortedMap<String, String> getMdc() {
484 return mdc;
485 }
486
487
488
489
490
491
492
493
494 @Deprecated
495 public Optional<Marker> getMarker() {
496 if (markers.isEmpty()) {
497 return empty();
498 }
499 if (markers.size() == 1) {
500 return Optional.of(markers.get(0));
501 }
502 throw new IllegalStateException("LoggingEvent has more than one marker");
503 }
504
505
506
507
508
509
510
511 public List<Marker> getMarkers() {
512 return markers;
513 }
514
515
516
517
518
519
520
521
522 public List<KeyValuePair> getKeyValuePairs() {
523 return keyValuePairs;
524 }
525
526 public String getMessage() {
527 return message;
528 }
529
530
531
532
533
534
535 public List<Object> getArguments() {
536 return arguments;
537 }
538
539 public Optional<Throwable> getThrowable() {
540 return throwable;
541 }
542
543
544
545
546
547 public TestLogger getCreatingLogger() {
548 return creatingLogger.get();
549 }
550
551
552
553
554 public Instant getTimestamp() {
555 return timestamp;
556 }
557
558
559
560
561 public String getThreadName() {
562 return threadName;
563 }
564
565
566
567
568 public ClassLoader getThreadContextClassLoader() {
569 return threadContextClassLoader;
570 }
571
572 void print() {
573 final PrintStream output = printStreamForLevel();
574 output.println(formatLogStatement());
575 throwable.ifPresent(throwableToPrint -> throwableToPrint.printStackTrace(output));
576 }
577
578 private String formatLogStatement() {
579 return ISO_FORMAT.format(getTimestamp())
580 + " ["
581 + getThreadName()
582 + "] "
583 + getLevel()
584 + safeLoggerName()
585 + " - "
586 + getFormattedMessage();
587 }
588
589 private String safeLoggerName() {
590 return creatingLogger.map(logger -> " " + logger.getName()).orElse("");
591 }
592
593 public String getFormattedMessage() {
594 Object[] argumentsWithNulls = getArguments().toArray();
595 return MessageFormatter.arrayFormat(getMessage(), argumentsWithNulls).getMessage();
596 }
597
598 private PrintStream printStreamForLevel() {
599 switch (level) {
600 case ERROR:
601 case WARN:
602 return System.err;
603 default:
604 return System.out;
605 }
606 }
607
608 @Override
609 public boolean equals(Object o) {
610 if (this == o) {
611 return true;
612 }
613 if (o == null || getClass() != o.getClass()) {
614 return false;
615 }
616 LoggingEvent that = (LoggingEvent) o;
617 return level == that.level
618 && Objects.equals(mdc, that.mdc)
619 && Objects.equals(markers, that.markers)
620 && Objects.equals(keyValuePairs, that.keyValuePairs)
621 && Objects.equals(throwable, that.throwable)
622 && Objects.equals(message, that.message)
623 && Objects.equals(arguments, that.arguments);
624 }
625
626 @Override
627 public int hashCode() {
628 return Objects.hash(level, mdc, markers, keyValuePairs, throwable, message, arguments);
629 }
630
631 @Override
632 public String toString() {
633 return "LoggingEvent{"
634 + "level="
635 + level
636 + ", mdc="
637 + mdc
638 + ", markers="
639 + markers
640 + ", keyValuePairs="
641 + keyValuePairs
642 + ", throwable="
643 + throwable
644 + ", message="
645 + (message == null ? "null" : '\'' + message + '\'')
646 + ", arguments="
647 + arguments
648 + '}';
649 }
650 }