1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j;
19  
20  import junit.framework.TestCase;
21  import org.apache.log4j.util.AbsoluteDateAndTimeFilter;
22  import org.apache.log4j.util.AbsoluteTimeFilter;
23  import org.apache.log4j.util.Compare;
24  import org.apache.log4j.util.ControlFilter;
25  import org.apache.log4j.util.Filter;
26  import org.apache.log4j.util.ISO8601Filter;
27  import org.apache.log4j.util.EnhancedJunitTestRunnerFilter;
28  import org.apache.log4j.util.EnhancedLineNumberFilter;
29  import org.apache.log4j.util.RelativeTimeFilter;
30  import org.apache.log4j.util.SunReflectFilter;
31  import org.apache.log4j.util.Transformer;
32  import org.apache.log4j.MDCOrderFilter;
33  import org.apache.log4j.spi.ThrowableInformation;
34  
35  import java.text.ParsePosition;
36  import java.text.SimpleDateFormat;
37  import java.util.Date;
38  import java.util.TimeZone;
39  import java.io.*;
40  
41  
42  public class EnhancedPatternLayoutTestCase extends TestCase {
43    static String TEMP = "output/temp";
44    static String FILTERED = "output/filtered";
45    static String EXCEPTION1 = "java.lang.Exception: Just testing";
46    static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
47    static String EXCEPTION3 = "\\s*at .*\\((Native Method|Unknown Source)\\)";
48    static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
49  
50    static String PAT0 =
51      "\\[main]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d{1,2}";
52    static String PAT1 = Filter.ISO8601_PAT + " " + PAT0;
53    static String PAT2 = Filter.ABSOLUTE_DATE_AND_TIME_PAT + " " + PAT0;
54    static String PAT3 = Filter.ABSOLUTE_TIME_PAT + " " + PAT0;
55    static String PAT4 = Filter.RELATIVE_TIME_PAT + " " + PAT0;
56    static String PAT5 =
57      "\\[main]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* : Message \\d{1,2}";
58    static String PAT6 =
59      "\\[main]\\ (DEBUG|INFO |WARN |ERROR|FATAL) org.apache.log4j.EnhancedPatternLayoutTestCase.common\\(EnhancedPatternLayoutTestCase.java(:\\d{1,4})?\\): Message \\d{1,2}";
60    static String PAT11a =
61      "^(DEBUG|INFO |WARN |ERROR|FATAL) \\[main]\\ log4j.EnhancedPatternLayoutTest: Message \\d{1,2}";
62    static String PAT11b =
63      "^(DEBUG|INFO |WARN |ERROR|FATAL) \\[main]\\ root: Message \\d{1,2}";
64    static String PAT12 =
65      "^\\[main]\\ (DEBUG|INFO |WARN |ERROR|FATAL) "
66      + "org.apache.log4j.EnhancedPatternLayoutTestCase.common\\(EnhancedPatternLayoutTestCase.java:\\d{3}\\): "
67      + "Message \\d{1,2}";
68    static String PAT13 =
69      "^\\[main]\\ (DEBUG|INFO |WARN |ERROR|FATAL) "
70      + "apache.log4j.EnhancedPatternLayoutTestCase.common\\(EnhancedPatternLayoutTestCase.java:\\d{3}\\): "
71      + "Message \\d{1,2}";
72    static String PAT14 =
73      "^(TRACE|DEBUG| INFO| WARN|ERROR|FATAL)\\ \\d{1,2}\\ *- Message \\d{1,2}";
74    static String PAT_MDC_1 = "";
75    Logger root;
76    Logger logger;
77    
78  
79    public EnhancedPatternLayoutTestCase(final String name) {
80      super(name);
81    }
82  
83    public void setUp() {
84      root = Logger.getRootLogger();
85      logger = Logger.getLogger(EnhancedPatternLayoutTest.class);
86    }
87  
88    public void tearDown() {
89      root.getLoggerRepository().resetConfiguration();
90    }
91  
92      /**
93       * Configures log4j from a properties file resource in class loader path.
94       * @param fileName resource name, only last element is significant.
95       * @throws IOException if resource not found or error reading resource.
96       */
97    private static void configure(final String fileName) throws IOException {
98          PropertyConfigurator.configure(fileName);
99    }
100 
101     /**
102      * Compares actual and expected files.
103      * @param actual file name for file generated by test
104      * @param expected resource name containing expected output
105      * @return true if files are the same after adjustments
106      * @throws IOException if IO error during comparison.
107      */
108   private static boolean compare(final String actual,
109                                  final String expected) throws IOException {
110       return Compare.compare(actual, expected);
111   }
112 
113   public void test1() throws Exception {
114     configure("input/pattern/enhancedPatternLayout1.properties");
115     common();
116     Transformer.transform(
117       TEMP, FILTERED,
118       new Filter[] {
119         new EnhancedLineNumberFilter(), new SunReflectFilter(),
120         new EnhancedJunitTestRunnerFilter()
121       });
122     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.1"));
123   }
124 
125   public void test2() throws Exception {
126     configure("input/pattern/enhancedPatternLayout2.properties");
127     common();
128 
129     ControlFilter cf1 =
130       new ControlFilter(
131         new String[] { PAT1, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
132     Transformer.transform(
133       TEMP, FILTERED,
134       new Filter[] {
135         cf1, new EnhancedLineNumberFilter(), new ISO8601Filter(),
136         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
137       });
138     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.2"));
139   }
140 
141   public void test3() throws Exception {
142     configure("input/pattern/enhancedPatternLayout3.properties");
143     common();
144 
145     ControlFilter cf1 =
146       new ControlFilter(
147         new String[] { PAT1, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
148     Transformer.transform(
149       TEMP, FILTERED,
150       new Filter[] {
151         cf1, new EnhancedLineNumberFilter(), new ISO8601Filter(),
152         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
153       });
154     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.3"));
155   }
156 
157   // Output format:
158   // 06 avr. 2002 18:30:58,937 [main] DEBUG atternLayoutTest - Message 0  
159   public void test4() throws Exception {
160     configure("input/pattern/enhancedPatternLayout4.properties");
161     common();
162 
163     ControlFilter cf1 =
164       new ControlFilter(
165         new String[] { PAT2, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
166     Transformer.transform(
167       TEMP, FILTERED,
168       new Filter[] {
169         cf1, new EnhancedLineNumberFilter(), new AbsoluteDateAndTimeFilter(),
170         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
171       });
172     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.4"));
173   }
174 
175   public void test5() throws Exception {
176     configure("input/pattern/enhancedPatternLayout5.properties");
177     common();
178 
179     ControlFilter cf1 =
180       new ControlFilter(
181         new String[] { PAT2, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
182     Transformer.transform(
183       TEMP, FILTERED,
184       new Filter[] {
185         cf1, new EnhancedLineNumberFilter(), new AbsoluteDateAndTimeFilter(),
186         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
187       });
188     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.5"));
189   }
190 
191   // 18:54:19,201 [main] DEBUG atternLayoutTest - Message 0
192   public void test6() throws Exception {
193     configure("input/pattern/enhancedPatternLayout6.properties");
194     common();
195 
196     ControlFilter cf1 =
197       new ControlFilter(
198         new String[] { PAT3, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
199     Transformer.transform(
200       TEMP, FILTERED,
201       new Filter[] {
202         cf1, new EnhancedLineNumberFilter(), new AbsoluteTimeFilter(),
203         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
204       });
205     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.6"));
206   }
207 
208   public void test7() throws Exception {
209     configure("input/pattern/enhancedPatternLayout7.properties");
210     common();
211 
212     ControlFilter cf1 =
213       new ControlFilter(
214         new String[] { PAT3, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
215     Transformer.transform(
216       TEMP, FILTERED,
217       new Filter[] {
218         cf1, new EnhancedLineNumberFilter(), new AbsoluteTimeFilter(),
219         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
220       });
221     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.7"));
222   }
223 
224   public void test8() throws Exception {
225     configure("input/pattern/enhancedPatternLayout8.properties");
226     common();
227 
228     ControlFilter cf1 =
229       new ControlFilter(
230         new String[] { PAT4, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
231     Transformer.transform(
232       TEMP, FILTERED,
233       new Filter[] {
234         cf1, new EnhancedLineNumberFilter(), new RelativeTimeFilter(),
235         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
236       });
237     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.8"));
238   }
239 
240   public void test9() throws Exception {
241     configure("input/pattern/enhancedPatternLayout9.properties");
242     common();
243 
244     ControlFilter cf1 =
245       new ControlFilter(
246         new String[] { PAT5, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
247     Transformer.transform(
248       TEMP, FILTERED,
249       new Filter[] {
250         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
251         new EnhancedJunitTestRunnerFilter()
252       });
253     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.9"));
254   }
255 
256   public void test10() throws Exception {
257     configure("input/pattern/enhancedPatternLayout10.properties");
258     common();
259 
260     ControlFilter cf1 =
261       new ControlFilter(
262         new String[] { PAT6, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
263     Transformer.transform(
264       TEMP, FILTERED,
265       new Filter[] {
266         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
267         new EnhancedJunitTestRunnerFilter()
268       });
269     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.10"));
270   }
271 
272   public void test11() throws Exception {
273     configure("input/pattern/enhancedPatternLayout11.properties");
274     common();
275 
276     ControlFilter cf1 =
277       new ControlFilter(
278         new String[] { PAT11a, PAT11b, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
279     Transformer.transform(
280       TEMP, FILTERED,
281       new Filter[] {
282         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
283         new EnhancedJunitTestRunnerFilter()
284       });
285     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.11"));
286   }
287 
288   public void test12() throws Exception {
289     configure("input/pattern/enhancedPatternLayout12.properties");
290     common();
291 
292     ControlFilter cf1 =
293       new ControlFilter(
294         new String[] { PAT12, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
295     Transformer.transform(
296       TEMP, FILTERED,
297       new Filter[] {
298         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
299         new EnhancedJunitTestRunnerFilter()
300       });
301     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.12"));
302   }
303 
304   public void test13() throws Exception {
305     configure("input/pattern/enhancedPatternLayout13.properties");
306     common();
307 
308     ControlFilter cf1 =
309       new ControlFilter(
310         new String[] { PAT13, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
311     Transformer.transform(
312       TEMP, FILTERED,
313       new Filter[] {
314         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
315         new EnhancedJunitTestRunnerFilter()
316       });
317     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.13"));
318   }
319 
320     /**
321      * Test of class abbreviation.
322      *
323      * @throws Exception
324      */
325     public void test14() throws Exception {
326       configure("input/pattern/enhancedPatternLayout14.properties");
327       common();
328 
329       Transformer.transform(
330         TEMP, FILTERED,
331         new Filter[] {
332           new EnhancedLineNumberFilter(), new SunReflectFilter(),
333           new EnhancedJunitTestRunnerFilter()
334         });
335       assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.14"));
336     }
337 
338 
339     private static void clearMDC() throws Exception {
340         java.util.Hashtable context = MDC.getContext();
341         if (context != null) {
342             context.clear();
343         }
344     }
345 
346   public void testMDC1() throws Exception {
347     configure("input/pattern/enhancedPatternLayout.mdc.1.properties");
348     clearMDC();
349     MDC.put("key1", "va11");
350     MDC.put("key2", "va12");
351     logger.debug("Hello World");
352     MDC.remove("key1");
353     MDC.remove("key2");
354 
355     Transformer.transform(
356       TEMP, FILTERED,
357       new Filter[] {
358         new EnhancedLineNumberFilter(), new SunReflectFilter(),
359         new EnhancedJunitTestRunnerFilter(),
360         new MDCOrderFilter()
361       });
362     assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.mdc.1"));
363   }
364     /**
365      * Tests log4j 1.2 style extension of EnhancedPatternLayout.
366      * Was test14 in log4j 1.2.
367      * @throws Exception
368      */
369     public void test15() throws Exception {
370       configure("input/pattern/enhancedPatternLayout15.properties");
371       common();
372       ControlFilter cf1 = new ControlFilter(new String[]{PAT14, EXCEPTION1,
373                                  EXCEPTION2, EXCEPTION3, EXCEPTION4});
374       Transformer.transform(
375         TEMP, FILTERED,
376         new Filter[] {
377           cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
378           new EnhancedJunitTestRunnerFilter()
379         });
380       assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.15"));
381     }
382     /**
383      * Tests explicit UTC time zone in pattern.
384      * @throws Exception
385      */
386     public void test16() throws Exception {
387       final long start = new Date().getTime();
388       configure("input/pattern/enhancedPatternLayout16.properties");
389       common();
390       final long end = new Date().getTime();
391       FileReader reader = new FileReader("output/patternLayout16.log");
392       char chars[] = new char[50];
393       reader.read(chars, 0, chars.length);
394       reader.close();
395       SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
396       format.setTimeZone(TimeZone.getTimeZone("GMT+0"));
397       String utcStr = new String(chars, 0, 19);
398       Date utcDate = format.parse(utcStr, new ParsePosition(0));
399       assertTrue(utcDate.getTime() >= start - 1000 && utcDate.getTime() < end + 1000);
400       String cstStr = new String(chars, 21, 19);
401       format.setTimeZone(TimeZone.getTimeZone("GMT-6"));
402       Date cstDate = format.parse(cstStr, new ParsePosition(0));
403       assertFalse(cstStr.equals(utcStr));
404       assertTrue(cstDate.getTime() >= start - 1000 && cstDate.getTime() < end + 1000);
405     }
406 
407   void common() {
408     int i = -1;
409 
410     logger.debug("Message " + ++i);
411     root.debug("Message " + i);
412 
413     logger.info("Message " + ++i);
414     root.info("Message " + i);
415 
416     logger.warn("Message " + ++i);
417     root.warn("Message " + i);
418 
419     logger.error("Message " + ++i);
420     root.error("Message " + i);
421 
422     logger.log(Level.FATAL, "Message " + ++i);
423     root.log(Level.FATAL, "Message " + i);
424 
425     Exception e = new Exception("Just testing");
426 
427     logger.debug("Message " + ++i, e);
428     logger.info("Message " + ++i, e);
429     logger.warn("Message " + ++i, e);
430     logger.error("Message " + ++i, e);
431     logger.log(Level.FATAL, "Message " + ++i, e);
432   }
433 
434   /**
435     Test case for MDC conversion pattern. */
436   public void testMDC2() throws Exception {
437     String OUTPUT_FILE   = "output/patternLayout.mdc.2";
438     String WITNESS_FILE  = "witness/pattern/enhancedPatternLayout.mdc.2";
439     
440     String mdcMsgPattern1 = "%m : %X%n";
441     String mdcMsgPattern2 = "%m : %X{key1}%n";
442     String mdcMsgPattern3 = "%m : %X{key2}%n";
443     String mdcMsgPattern4 = "%m : %X{key3}%n";
444     String mdcMsgPattern5 = "%m : %X{key1},%X{key2},%X{key3}%n";
445     
446     // set up appender
447     EnhancedPatternLayout layout = new EnhancedPatternLayout("%m%n");
448     Appender appender = new FileAppender(layout, OUTPUT_FILE, false);
449             
450     // set appender on root and set level to debug
451     root.addAppender(appender);
452     root.setLevel(Level.DEBUG);
453 
454     clearMDC();
455     // output starting message
456     root.debug("starting mdc pattern test");
457  
458     layout.setConversionPattern(mdcMsgPattern1);
459     layout.activateOptions();
460     root.debug("empty mdc, no key specified in pattern");
461     
462     layout.setConversionPattern(mdcMsgPattern2);
463     layout.activateOptions();
464     root.debug("empty mdc, key1 in pattern");
465     
466     layout.setConversionPattern(mdcMsgPattern3);
467     layout.activateOptions();
468     root.debug("empty mdc, key2 in pattern");
469     
470     layout.setConversionPattern(mdcMsgPattern4);
471     layout.activateOptions();
472     root.debug("empty mdc, key3 in pattern");
473     
474     layout.setConversionPattern(mdcMsgPattern5);
475     layout.activateOptions();
476     root.debug("empty mdc, key1, key2, and key3 in pattern");
477 
478     MDC.put("key1", "value1");
479     MDC.put("key2", "value2");
480 
481     layout.setConversionPattern(mdcMsgPattern1);
482     layout.activateOptions();
483     root.debug("filled mdc, no key specified in pattern");
484     
485     layout.setConversionPattern(mdcMsgPattern2);
486     layout.activateOptions();
487     root.debug("filled mdc, key1 in pattern");
488     
489     layout.setConversionPattern(mdcMsgPattern3);
490     layout.activateOptions();
491     root.debug("filled mdc, key2 in pattern");
492     
493     layout.setConversionPattern(mdcMsgPattern4);
494     layout.activateOptions();
495     root.debug("filled mdc, key3 in pattern");
496     
497     layout.setConversionPattern(mdcMsgPattern5);
498     layout.activateOptions();
499     root.debug("filled mdc, key1, key2, and key3 in pattern");
500 
501     MDC.remove("key1");
502     MDC.remove("key2");
503 
504     layout.setConversionPattern("%m%n");
505     layout.activateOptions();
506     root.debug("finished mdc pattern test");
507 
508 
509       Transformer.transform(
510         OUTPUT_FILE, FILTERED,
511         new Filter[] {
512           new EnhancedLineNumberFilter(), new SunReflectFilter(),
513           new EnhancedJunitTestRunnerFilter(),
514           new MDCOrderFilter()
515         });
516 
517     assertTrue(compare(FILTERED, WITNESS_FILE));
518   }
519   /**
520     Test case for throwable conversion pattern. */
521   public void testThrowable() throws Exception {
522     String OUTPUT_FILE   = "output/patternLayout.throwable";
523     String WITNESS_FILE  = "witness/pattern/enhancedPatternLayout.throwable";
524     
525     
526     // set up appender
527     EnhancedPatternLayout layout = new EnhancedPatternLayout("%m%n");
528     Appender appender = new FileAppender(layout, OUTPUT_FILE, false);
529             
530     // set appender on root and set level to debug
531     root.addAppender(appender);
532     root.setLevel(Level.DEBUG);
533     
534     // output starting message
535     root.debug("starting throwable pattern test");
536      Exception ex = new Exception("Test Exception");
537     root.debug("plain pattern, no exception");
538     root.debug("plain pattern, with exception", ex);
539     layout.setConversionPattern("%m%n%throwable");
540     layout.activateOptions();
541     root.debug("%throwable, no exception");
542     root.debug("%throwable, with exception", ex);
543 
544     layout.setConversionPattern("%m%n%throwable{short}");
545     layout.activateOptions();
546     root.debug("%throwable{short}, no exception");
547     root.debug("%throwable{short}, with exception", ex);
548 
549     layout.setConversionPattern("%m%n%throwable{none}");
550     layout.activateOptions();
551     root.debug("%throwable{none}, no exception");
552     root.debug("%throwable{none}, with exception", ex);
553 
554     layout.setConversionPattern("%m%n%throwable{0}");
555     layout.activateOptions();
556     root.debug("%throwable{0}, no exception");
557     root.debug("%throwable{0}, with exception", ex);
558 
559     layout.setConversionPattern("%m%n%throwable{1}");
560     layout.activateOptions();
561     root.debug("%throwable{1}, no exception");
562     root.debug("%throwable{1}, with exception", ex);
563 
564     layout.setConversionPattern("%m%n%throwable{100}");
565     layout.activateOptions();
566     root.debug("%throwable{100}, no exception");
567     root.debug("%throwable{100}, with exception", ex);
568 
569     //
570     //  manufacture a pattern to get just the first two lines
571     //
572     String[] trace = new ThrowableInformation(ex).getThrowableStrRep();
573     layout.setConversionPattern("%m%n%throwable{" + (2 - trace.length) + "}");
574     layout.activateOptions();
575     root.debug("%throwable{-n}, no exception");
576     root.debug("%throwable{-n}, with exception", ex);
577 
578 
579       Transformer.transform(
580         OUTPUT_FILE, FILTERED,
581         new Filter[] {
582           new EnhancedLineNumberFilter(), new SunReflectFilter(),
583           new EnhancedJunitTestRunnerFilter(),
584           new MDCOrderFilter()
585         });
586 
587     assertTrue(compare(FILTERED, WITNESS_FILE));
588   }
589 }