View Javadoc
1   package uk.org.lidalia.slf4jtest;
2   
3   import java.io.PrintStream;
4   import java.util.Collections;
5   import java.util.Map;
6   
7   import org.joda.time.Instant;
8   import org.slf4j.Marker;
9   import org.slf4j.helpers.MessageFormatter;
10  
11  import com.google.common.base.Function;
12  import com.google.common.base.Optional;
13  import com.google.common.collect.ImmutableList;
14  import com.google.common.collect.ImmutableMap;
15  
16  import uk.org.lidalia.lang.Identity;
17  import uk.org.lidalia.lang.RichObject;
18  import uk.org.lidalia.slf4jext.Level;
19  
20  import static com.google.common.base.Optional.absent;
21  import static com.google.common.base.Optional.fromNullable;
22  import static com.google.common.base.Preconditions.checkNotNull;
23  import static com.google.common.collect.FluentIterable.from;
24  import static java.util.Arrays.asList;
25  
26  /**
27   * Representation of a call to a logger for test assertion purposes.
28   * <p/>
29   * The contract of {@link #equals(Object)} and {@link #hashCode} is that they compare the results of:
30   * <ul>
31   * <li>{@link #getLevel()}</li>
32   * <li>{@link #getMdc()}</li>
33   * <li>{@link #getMarker()}</li>
34   * <li>{@link #getThrowable()}</li>
35   * <li>{@link #getMessage()}</li>
36   * <li>{@link #getArguments()}</li>
37   * </ul>
38   * <p/>
39   * They do NOT compare the results of {@link #getTimestamp()} or {@link #getCreatingLogger()} as this would render it impractical
40   * to create appropriate expected {@link LoggingEvent}s to compare against.
41   * <p/>
42   * Constructors and convenient static factory methods exist to create {@link LoggingEvent}s with appropriate
43   * defaults.  These are not documented further as they should be self-evident.
44   */
45  @SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods" })
46  public class LoggingEvent extends RichObject {
47  
48      public static LoggingEvent trace(final String message, final Object... arguments) {
49          return new LoggingEvent(Level.TRACE, message, arguments);
50      }
51  
52      public static LoggingEvent trace(final Throwable throwable, final String message, final Object... arguments) {
53          return new LoggingEvent(Level.TRACE, throwable, message, arguments);
54      }
55  
56      public static LoggingEvent trace(final Marker marker, final String message, final Object... arguments) {
57          return new LoggingEvent(Level.TRACE, marker, message, arguments);
58      }
59  
60      public static LoggingEvent trace(
61              final Marker marker, final Throwable throwable, final String message, final Object... arguments) {
62          return new LoggingEvent(Level.TRACE, marker, throwable, message, arguments);
63      }
64  
65      public static LoggingEvent trace(final Map<String, String> mdc, final String message, final Object... arguments) {
66          return new LoggingEvent(Level.TRACE, mdc, message, arguments);
67      }
68  
69      public static LoggingEvent trace(
70              final Map<String, String> mdc, final Throwable throwable, final String message, final Object... arguments) {
71          return new LoggingEvent(Level.TRACE, mdc, throwable, message, arguments);
72      }
73  
74      public static LoggingEvent trace(
75              final Map<String, String> mdc, final Marker marker, final String message, final Object... arguments) {
76          return new LoggingEvent(Level.TRACE, mdc, marker, message, arguments);
77      }
78  
79      public static LoggingEvent trace(
80              final Map<String, String> mdc,
81              final Marker marker,
82              final Throwable throwable,
83              final String message,
84              final Object... arguments) {
85          return new LoggingEvent(Level.TRACE, mdc, marker, throwable, message, arguments);
86      }
87  
88      public static LoggingEvent debug(final String message, final Object... arguments) {
89          return new LoggingEvent(Level.DEBUG, message, arguments);
90      }
91  
92      public static LoggingEvent debug(final Throwable throwable, final String message, final Object... arguments) {
93          return new LoggingEvent(Level.DEBUG, throwable, message, arguments);
94      }
95  
96      public static LoggingEvent debug(final Marker marker, final String message, final Object... arguments) {
97          return new LoggingEvent(Level.DEBUG, marker, message, arguments);
98      }
99  
100     public static LoggingEvent debug(
101             final Marker marker, final Throwable throwable, final String message, final Object... arguments) {
102         return new LoggingEvent(Level.DEBUG, marker, throwable, message, arguments);
103     }
104 
105     public static LoggingEvent debug(final Map<String, String> mdc, final String message, final Object... arguments) {
106         return new LoggingEvent(Level.DEBUG, mdc, message, arguments);
107     }
108 
109     public static LoggingEvent debug(
110             final Map<String, String> mdc, final Throwable throwable, final String message, final Object... arguments) {
111         return new LoggingEvent(Level.DEBUG, mdc, throwable, message, arguments);
112     }
113 
114     public static LoggingEvent debug(
115             final Map<String, String> mdc, final Marker marker, final String message, final Object... arguments) {
116         return new LoggingEvent(Level.DEBUG, mdc, marker, message, arguments);
117     }
118 
119     public static LoggingEvent debug(
120             final Map<String, String> mdc,
121             final Marker marker,
122             final Throwable throwable,
123             final String message,
124             final Object... arguments) {
125         return new LoggingEvent(Level.DEBUG, mdc, marker, throwable, message, arguments);
126     }
127 
128     public static LoggingEvent info(final String message, final Object... arguments) {
129         return new LoggingEvent(Level.INFO, message, arguments);
130     }
131 
132     public static LoggingEvent info(final Throwable throwable, final String message, final Object... arguments) {
133         return new LoggingEvent(Level.INFO, throwable, message, arguments);
134     }
135 
136     public static LoggingEvent info(final Marker marker, final String message, final Object... arguments) {
137         return new LoggingEvent(Level.INFO, marker, message, arguments);
138     }
139 
140     public static LoggingEvent info(
141             final Marker marker, final Throwable throwable, final String message, final Object... arguments) {
142         return new LoggingEvent(Level.INFO, marker, throwable, message, arguments);
143     }
144 
145     public static LoggingEvent info(final Map<String, String> mdc, final String message, final Object... arguments) {
146         return new LoggingEvent(Level.INFO, mdc, message, arguments);
147     }
148 
149     public static LoggingEvent info(
150             final Map<String, String> mdc, final Throwable throwable, final String message, final Object... arguments) {
151         return new LoggingEvent(Level.INFO, mdc, throwable, message, arguments);
152     }
153 
154     public static LoggingEvent info(
155             final Map<String, String> mdc, final Marker marker, final String message, final Object... arguments) {
156         return new LoggingEvent(Level.INFO, mdc, marker, message, arguments);
157     }
158 
159     public static LoggingEvent info(
160             final Map<String, String> mdc,
161             final Marker marker,
162             final Throwable throwable,
163             final String message,
164             final Object... arguments) {
165         return new LoggingEvent(Level.INFO, mdc, marker, throwable, message, arguments);
166     }
167 
168     public static LoggingEvent warn(final String message, final Object... arguments) {
169         return new LoggingEvent(Level.WARN, message, arguments);
170     }
171 
172     public static LoggingEvent warn(final Throwable throwable, final String message, final Object... arguments) {
173         return new LoggingEvent(Level.WARN, throwable, message, arguments);
174     }
175 
176     public static LoggingEvent warn(final Marker marker, final String message, final Object... arguments) {
177         return new LoggingEvent(Level.WARN, marker, message, arguments);
178     }
179 
180     public static LoggingEvent warn(
181             final Marker marker, final Throwable throwable, final String message, final Object... arguments) {
182         return new LoggingEvent(Level.WARN, marker, throwable, message, arguments);
183     }
184 
185     public static LoggingEvent warn(final Map<String, String> mdc, final String message, final Object... arguments) {
186         return new LoggingEvent(Level.WARN, mdc, message, arguments);
187     }
188 
189     public static LoggingEvent warn(
190             final Map<String, String> mdc, final Throwable throwable, final String message, final Object... arguments) {
191         return new LoggingEvent(Level.WARN, mdc, throwable, message, arguments);
192     }
193 
194     public static LoggingEvent warn(
195             final Map<String, String> mdc, final Marker marker, final String message, final Object... arguments) {
196         return new LoggingEvent(Level.WARN, mdc, marker, message, arguments);
197     }
198 
199     public static LoggingEvent warn(
200             final Map<String, String> mdc,
201             final Marker marker,
202             final Throwable throwable,
203             final String message,
204             final Object... arguments) {
205         return new LoggingEvent(Level.WARN, mdc, marker, throwable, message, arguments);
206     }
207 
208     public static LoggingEvent error(final String message, final Object... arguments) {
209         return new LoggingEvent(Level.ERROR, message, arguments);
210     }
211 
212     public static LoggingEvent error(final Throwable throwable, final String message, final Object... arguments) {
213         return new LoggingEvent(Level.ERROR, throwable, message, arguments);
214     }
215 
216     public static LoggingEvent error(final Marker marker, final String message, final Object... arguments) {
217         return new LoggingEvent(Level.ERROR, marker, message, arguments);
218     }
219 
220     public static LoggingEvent error(
221             final Marker marker, final Throwable throwable, final String message, final Object... arguments) {
222         return new LoggingEvent(Level.ERROR, marker, throwable, message, arguments);
223     }
224 
225     public static LoggingEvent error(final Map<String, String> mdc, final String message, final Object... arguments) {
226         return new LoggingEvent(Level.ERROR, mdc, message, arguments);
227     }
228 
229     public static LoggingEvent error(
230             final Map<String, String> mdc, final Throwable throwable, final String message, final Object... arguments) {
231         return new LoggingEvent(Level.ERROR, mdc, throwable, message, arguments);
232     }
233 
234     public static LoggingEvent error(
235             final Map<String, String> mdc, final Marker marker, final String message, final Object... arguments) {
236         return new LoggingEvent(Level.ERROR, mdc, marker, message, arguments);
237     }
238 
239     public static LoggingEvent error(
240             final Map<String, String> mdc,
241             final Marker marker,
242             final Throwable throwable,
243             final String message,
244             final Object... arguments) {
245         return new LoggingEvent(Level.ERROR, mdc, marker, throwable, message, arguments);
246     }
247 
248     public LoggingEvent(final Level level, final String message, final Object... arguments) {
249         this(level, Collections.<String, String>emptyMap(), Optional.<Marker>absent(), Optional.<Throwable>absent(),
250                 message, arguments);
251     }
252 
253     public LoggingEvent(final Level level, final Throwable throwable, final String message, final Object... arguments) {
254         this(level, Collections.<String, String>emptyMap(), Optional.<Marker>absent(), fromNullable(throwable),
255                 message, arguments);
256     }
257 
258     public LoggingEvent(final Level level, final Marker marker, final String message, final Object... arguments) {
259         this(level, Collections.<String, String>emptyMap(), fromNullable(marker), Optional.<Throwable>absent(),
260                 message, arguments);
261     }
262 
263     public LoggingEvent(
264             final Level level, final Marker marker, final Throwable throwable, final String message, final Object... arguments) {
265         this(level, Collections.<String, String>emptyMap(), fromNullable(marker), fromNullable(throwable), message, arguments);
266     }
267 
268     public LoggingEvent(final Level level, final Map<String, String> mdc, final String message, final Object... arguments) {
269         this(level, mdc, Optional.<Marker>absent(), Optional.<Throwable>absent(), message, arguments);
270     }
271 
272     public LoggingEvent(
273             final Level level,
274             final Map<String, String> mdc,
275             final Throwable throwable,
276             final String message,
277             final Object... arguments) {
278         this(level, mdc, Optional.<Marker>absent(), fromNullable(throwable), message, arguments);
279     }
280 
281     public LoggingEvent(
282             final Level level,
283             final Map<String, String> mdc,
284             final Marker marker,
285             final String message,
286             final Object... arguments) {
287         this(level, mdc, fromNullable(marker), Optional.<Throwable>absent(), message, arguments);
288     }
289 
290     public LoggingEvent(
291             final Level level,
292             final Map<String, String> mdc,
293             final Marker marker,
294             final Throwable throwable,
295             final String message,
296             final Object... arguments) {
297         this(level, mdc, fromNullable(marker), fromNullable(throwable), message, arguments);
298     }
299 
300     private LoggingEvent(
301             final Level level,
302             final Map<String, String> mdc,
303             final Optional<Marker> marker,
304             final Optional<Throwable> throwable,
305             final String message,
306             final Object... arguments) {
307         this(Optional.<TestLogger>absent(), level, mdc, marker, throwable, message, arguments);
308     }
309 
310     LoggingEvent(
311             final Optional<TestLogger> creatingLogger,
312             final Level level,
313             final Map<String, String> mdc,
314             final Optional<Marker> marker,
315             final Optional<Throwable> throwable,
316             final String message,
317             final Object... arguments) {
318         super();
319         this.creatingLogger = creatingLogger;
320         this.level = checkNotNull(level);
321         this.mdc = ImmutableMap.copyOf(mdc);
322         this.marker = checkNotNull(marker);
323         this.throwable = checkNotNull(throwable);
324         this.message = checkNotNull(message);
325         this.arguments = from(asList(arguments)).transform(TO_NON_NULL_VALUE).toList();
326     }
327 
328     private static final Function<Object, Object> TO_NON_NULL_VALUE = new Function<Object, Object>() {
329         @Override
330         public Object apply(final Object input) {
331             return fromNullable(input).or((Object) absent());
332         }
333     };
334 
335     @Identity private final Level level;
336     @Identity private final ImmutableMap<String, String> mdc;
337     @Identity private final Optional<Marker> marker;
338     @Identity private final Optional<Throwable> throwable;
339     @Identity private final String message;
340     @Identity private final ImmutableList<Object> arguments;
341 
342     private final Optional<TestLogger> creatingLogger;
343     private final Instant timestamp = new Instant();
344     private final String threadName = Thread.currentThread().getName();
345 
346     public Level getLevel() {
347         return level;
348     }
349 
350     public ImmutableMap<String, String> getMdc() {
351         return mdc;
352     }
353 
354     public Optional<Marker> getMarker() {
355         return marker;
356     }
357 
358     public String getMessage() {
359         return message;
360     }
361 
362     public ImmutableList<Object> getArguments() {
363         return arguments;
364     }
365 
366     public Optional<Throwable> getThrowable() {
367         return throwable;
368     }
369 
370     /**
371      * @return the logger that created this logging event.
372      * @throws IllegalStateException if this logging event was not created by a logger
373      */
374     public TestLogger getCreatingLogger() {
375         return creatingLogger.get();
376     }
377 
378     /**
379      * @return the time at which this logging event was created
380      */
381     public Instant getTimestamp() {
382         return timestamp;
383     }
384 
385     /**
386      * @return the name of the thread that created this logging event
387      */
388     public String getThreadName() {
389         return threadName;
390     }
391 
392     void print() {
393         final PrintStream output = printStreamForLevel();
394         output.println(formatLogStatement());
395         throwable.transform(printThrowableTo(output));
396     }
397 
398     private static Function<Throwable, String> printThrowableTo(final PrintStream output) {
399         return new Function<Throwable, String>() {
400             @Override
401             public String apply(final Throwable throwableToPrint) {
402                 throwableToPrint.printStackTrace(output);
403                 return "";
404             }
405         };
406     }
407 
408     private String formatLogStatement() {
409         return getTimestamp() + " [" + getThreadName() + "] " + getLevel() + safeLoggerName() + " - " + getFormattedMessage();
410     }
411 
412     private String safeLoggerName() {
413         return creatingLogger.transform(toLoggerNameString).or("");
414     }
415 
416     private static final Function<TestLogger, String> toLoggerNameString = new Function<TestLogger, String>() {
417         @Override
418         public String apply(final TestLogger logger) {
419             return " " + logger.getName();
420         }
421     };
422 
423     private String getFormattedMessage() {
424         return MessageFormatter.arrayFormat(getMessage(), getArguments().toArray()).getMessage();
425     }
426 
427     private PrintStream printStreamForLevel() {
428         switch (level) {
429             case ERROR:
430             case WARN:
431                 return System.err;
432             default:
433                 return System.out;
434         }
435     }
436 }