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.net;
19  
20  import junit.framework.TestCase;
21  
22  import org.apache.log4j.AsyncAppender;
23  import org.apache.log4j.Layout;
24  import org.apache.log4j.Level;
25  import org.apache.log4j.LogManager;
26  import org.apache.log4j.Logger;
27  import org.apache.log4j.PatternLayout;
28  import org.apache.log4j.VectorErrorHandler;
29  import org.apache.log4j.HTMLLayout;
30  
31  import java.util.StringTokenizer;
32  import java.net.DatagramSocket;
33  import java.net.DatagramPacket;
34  import java.text.SimpleDateFormat;
35  import java.util.Locale;
36  import java.util.Date;
37  import java.util.Calendar;
38  
39  
40  /**
41   *    Tests for SyslogAppender
42   *
43   *
44   * */
45  public class SyslogAppenderTest extends TestCase {
46    /**
47     * Create new instance of SyslogAppenderTest.
48     * @param testName test name
49     */
50    public SyslogAppenderTest(final String testName) {
51      super(testName);
52    }
53  
54    /**
55      * Resets configuration after every test.
56    */
57    public void tearDown() {
58      LogManager.resetConfiguration();
59    }
60  
61    /**
62     * Test default constructor.
63     */
64    public void testDefaultConstructor() {
65      SyslogAppender appender = new SyslogAppender();
66      assertEquals("user", appender.getFacility());
67      assertEquals(false, appender.getFacilityPrinting());
68      assertNull(appender.getLayout());
69      assertNull(appender.getSyslogHost());
70      assertTrue(appender.requiresLayout());
71    }
72  
73    /**
74     * Test two parameter constructor.
75     */
76    public void testTwoParamConstructor() {
77      Layout layout = new PatternLayout();
78      SyslogAppender appender = new SyslogAppender(layout, 24);
79      assertEquals("daemon", appender.getFacility());
80      assertEquals(false, appender.getFacilityPrinting());
81      assertEquals(layout, appender.getLayout());
82      assertNull(appender.getSyslogHost());
83      assertTrue(appender.requiresLayout());
84    }
85  
86    /**
87     * Test two parameter constructor with unexpected facility.
88     */
89    public void testTwoParamConstructorBadFacility() {
90      Layout layout = new PatternLayout();
91      SyslogAppender appender = new SyslogAppender(layout, 25);
92      assertEquals("user", appender.getFacility());
93      assertEquals(false, appender.getFacilityPrinting());
94      assertEquals(layout, appender.getLayout());
95      assertNull(appender.getSyslogHost());
96      assertTrue(appender.requiresLayout());
97    }
98  
99    /**
100    * Test three parameter constructor.
101    */
102   public void testThreeParamConstructor() {
103     Layout layout = new PatternLayout();
104     SyslogAppender appender =
105       new SyslogAppender(layout, "syslog.example.org", 24);
106     assertEquals("daemon", appender.getFacility());
107     assertEquals(false, appender.getFacilityPrinting());
108     assertEquals(layout, appender.getLayout());
109     assertEquals("syslog.example.org", appender.getSyslogHost());
110     assertTrue(appender.requiresLayout());
111   }
112 
113   /**
114    * Test getFacilityString for expected facility codes.
115    */
116   public void testGetFacilityString() {
117     String expected =
118       "kern user mail daemon auth syslog lpr news "
119       + "uucp cron authpriv ftp local0 local1 local2 local3 "
120       + "local4 local5 local6 local7 ";
121     StringBuffer actual = new StringBuffer();
122 
123     for (int i = 0; i <= 11; i++) {
124       actual.append(SyslogAppender.getFacilityString(i << 3));
125       actual.append(' ');
126     }
127 
128     for (int i = 16; i <= 23; i++) {
129       actual.append(SyslogAppender.getFacilityString(i << 3));
130       actual.append(' ');
131     }
132 
133     assertEquals(expected, actual.toString());
134   }
135 
136   /**
137    * Test getFacilityString for some unexpected facility codes.
138    */
139   public void testGetFacilityStringUnexpected() {
140     assertNull(SyslogAppender.getFacilityString(1));
141     assertNull(SyslogAppender.getFacilityString(12 << 3));
142   }
143 
144   /**
145    * Test getFacility with a bogus facility name.
146    */
147   public void testGetFacilityBogus() {
148     assertEquals(-1, SyslogAppender.getFacility("bogus"));
149   }
150 
151   /**
152    * Test getFacility with a null facility name.
153    */
154   public void testGetFacilityNull() {
155     assertEquals(-1, SyslogAppender.getFacility(null));
156   }
157 
158   /**
159    * Test getFacility for expected system facility names.
160    */
161   public void testGetFacilitySystemNames() {
162     String[] names =
163       new String[] {
164         "kErn", "usEr", "MaIL", "daemOn", "auTh", "syslOg", "lPr", "newS",
165         "Uucp", "croN", "authprIv", "ftP"
166       };
167 
168     for (int i = 0; i <= 11; i++) {
169       assertEquals(i << 3, SyslogAppender.getFacility(names[i]));
170     }
171   }
172 
173   /**
174    * Test getFacility for expected system facility names.
175    */
176   public void testGetFacilityLocalNames() {
177     String[] names =
178       new String[] {
179         "lOcal0", "LOCAL1", "loCal2", "locAl3", "locaL4", "local5", "LOCal6",
180         "loCAL7"
181       };
182 
183     for (int i = 0; i <= 7; i++) {
184       assertEquals((16 + i) << 3, SyslogAppender.getFacility(names[i]));
185     }
186   }
187 
188   /**
189    * Test setFacilityPrinting.
190    */
191   public void testSetFacilityPrinting() {
192     SyslogAppender appender = new SyslogAppender();
193     assertFalse(appender.getFacilityPrinting());
194     appender.setFacilityPrinting(true);
195     assertTrue(appender.getFacilityPrinting());
196     appender.setFacilityPrinting(false);
197     assertFalse(appender.getFacilityPrinting());
198   }
199 
200   /**
201    * Test of SyslogAppender constants.
202    */
203   public void testConstants() {
204     assertEquals(0 << 3, SyslogAppender.LOG_KERN);
205     assertEquals(1 << 3, SyslogAppender.LOG_USER);
206     assertEquals(2 << 3, SyslogAppender.LOG_MAIL);
207     assertEquals(3 << 3, SyslogAppender.LOG_DAEMON);
208     assertEquals(4 << 3, SyslogAppender.LOG_AUTH);
209     assertEquals(5 << 3, SyslogAppender.LOG_SYSLOG);
210     assertEquals(6 << 3, SyslogAppender.LOG_LPR);
211     assertEquals(7 << 3, SyslogAppender.LOG_NEWS);
212     assertEquals(8 << 3, SyslogAppender.LOG_UUCP);
213     assertEquals(9 << 3, SyslogAppender.LOG_CRON);
214     assertEquals(10 << 3, SyslogAppender.LOG_AUTHPRIV);
215     assertEquals(11 << 3, SyslogAppender.LOG_FTP);
216     assertEquals(16 << 3, SyslogAppender.LOG_LOCAL0);
217     assertEquals(17 << 3, SyslogAppender.LOG_LOCAL1);
218     assertEquals(18 << 3, SyslogAppender.LOG_LOCAL2);
219     assertEquals(19 << 3, SyslogAppender.LOG_LOCAL3);
220     assertEquals(20 << 3, SyslogAppender.LOG_LOCAL4);
221     assertEquals(21 << 3, SyslogAppender.LOG_LOCAL5);
222     assertEquals(22 << 3, SyslogAppender.LOG_LOCAL6);
223     assertEquals(23 << 3, SyslogAppender.LOG_LOCAL7);
224   }
225 
226   /**
227    * Test setFacility with null.
228    * Should have no effect.
229    */
230   public void testSetFacilityKern() {
231     SyslogAppender appender = new SyslogAppender();
232     appender.setFacility("kern");
233     appender.setFacility(null);
234     assertEquals("kern", appender.getFacility());
235   }
236 
237   /**
238    * Test setFacility with null.
239    * Should have no effect.
240    */
241   public void testSetFacilityNull() {
242     SyslogAppender appender = new SyslogAppender();
243     appender.setFacility("kern");
244     appender.setFacility(null);
245     assertEquals("kern", appender.getFacility());
246   }
247 
248   /**
249    * Test setFacility with bogus value.
250    * Should reset to user.
251    */
252   public void testSetFacilityBogus() {
253     SyslogAppender appender = new SyslogAppender();
254     appender.setFacility("kern");
255     appender.setFacility("bogus");
256     assertEquals("user", appender.getFacility());
257   }
258 
259   /**
260    * Tests calling setFacility after appender has been activated.
261    */
262   public void testSetFacilityAfterActivation() {
263     SyslogAppender appender = new SyslogAppender();
264     appender.setName("foo");
265     appender.setThreshold(Level.INFO);
266     appender.setSyslogHost("localhost");
267     appender.setFacility("user");
268     appender.setLayout(new PatternLayout("%m%n"));
269 
270     VectorErrorHandler errorHandler = new VectorErrorHandler();
271     appender.setErrorHandler(errorHandler);
272     appender.activateOptions();
273     appender.setFacility("kern");
274     assertEquals("kern", appender.getFacility());
275   }
276 
277   /**
278    * Tests that append method drops messages below threshold.
279    * Can't reach isSevereAsThreshold call in SyslogAppender.append
280    * since it is checked in AppenderSkeleton.doAppend.
281    */
282   public void testAppendBelowThreshold() {
283     SyslogAppender appender = new SyslogAppender();
284     appender.setThreshold(Level.ERROR);
285     appender.activateOptions();
286 
287     Logger logger = Logger.getRootLogger();
288     logger.addAppender(appender);
289     logger.info(
290       "Should not be logged by SyslogAppenderTest.testAppendBelowThreshold.");
291   }
292 
293   /**
294    * Tests that append method drops messages below threshold.
295    */
296   public void testAppendNoHost() {
297     SyslogAppender appender = new SyslogAppender();
298     appender.setName("foo");
299     appender.setThreshold(Level.INFO);
300 
301     VectorErrorHandler errorHandler = new VectorErrorHandler();
302     appender.setErrorHandler(errorHandler);
303     appender.setLayout(new PatternLayout("%m%n"));
304     appender.activateOptions();
305 
306     Logger logger = Logger.getRootLogger();
307     logger.addAppender(appender);
308     logger.info(
309       "Should not be logged by SyslogAppenderTest.testAppendNoHost.");
310     assertEquals(1, errorHandler.size());
311 
312     //
313     //  Appender is misspelled in implementation
314     //
315     assertEquals(
316       "No syslog host is set for SyslogAppedender named \"foo\".",
317       errorHandler.getMessage(0));
318   }
319 
320   /**
321    * Tests append method under normal conditions.
322    */
323   public void testAppend() {
324     SyslogAppender appender = new SyslogAppender();
325     appender.setName("foo");
326     appender.setThreshold(Level.INFO);
327     appender.setSyslogHost("localhost");
328     appender.setFacility("user");
329     appender.setLayout(new PatternLayout("%m%n"));
330 
331     VectorErrorHandler errorHandler = new VectorErrorHandler();
332     appender.setErrorHandler(errorHandler);
333     appender.activateOptions();
334 
335     //
336     //  wrap SyslogAppender with an Async since appender may
337     //    hang if syslogd is not accepting network messages
338     //
339     AsyncAppender asyncAppender = new AsyncAppender();
340     asyncAppender.addAppender(appender);
341     asyncAppender.activateOptions();
342 
343     Logger logger = Logger.getRootLogger();
344     logger.addAppender(asyncAppender);
345 
346     Exception e =
347       new Exception("Expected exception from SyslogAppenderTest.testAppend");
348     logger.info(
349       "Expected message from log4j unit test SyslogAppenderTest.testAppend.", e);
350     assertEquals(0, errorHandler.size());
351   }
352 
353   /**
354     *  Tests SyslogAppender with IPv6 address.
355     */
356   public void testIPv6() {
357       SyslogAppender appender = new SyslogAppender();
358       appender.setSyslogHost("::1");
359   }
360 
361   /**
362     *  Tests SyslogAppender with IPv6 address enclosed in square brackets.
363     */
364   public void testIPv6InBrackets() {
365       SyslogAppender appender = new SyslogAppender();
366       appender.setSyslogHost("[::1]");
367   }
368 
369   /**
370     *  Tests SyslogAppender with IPv6 address enclosed in square brackets
371     *     followed by port specification.
372     */
373   public void testIPv6AndPort() {
374       SyslogAppender appender = new SyslogAppender();
375       appender.setSyslogHost("[::1]:1514");
376   }
377 
378   /**
379     *  Tests SyslogAppender with host name enclosed in square brackets
380     *     followed by port specification.
381     */
382   public void testHostNameAndPort() {
383       SyslogAppender appender = new SyslogAppender();
384       appender.setSyslogHost("localhost:1514");
385   }
386 
387 
388   /**
389     *  Tests SyslogAppender with IPv4 address followed by port specification.
390     */
391   public void testIPv4AndPort() {
392       SyslogAppender appender = new SyslogAppender();
393       appender.setSyslogHost("127.0.0.1:1514");
394   }
395 
396     private static String[] log(final boolean header,
397                                 final String msg,
398                                 final Exception ex,
399                                 final int packets) throws Exception {
400         DatagramSocket ds = new DatagramSocket();
401         ds.setSoTimeout(2000);
402 
403       SyslogAppender appender = new SyslogAppender();
404       appender.setSyslogHost("localhost:" + ds.getLocalPort());
405       appender.setName("name");
406       appender.setHeader(header);
407       PatternLayout pl = new PatternLayout("%m");
408       appender.setLayout(pl);
409       appender.activateOptions();
410 
411       Logger l = Logger.getRootLogger();
412       l.addAppender(appender);
413       if (ex == null) {
414         l.info(msg);
415       } else {
416         l.error(msg, ex);
417       }
418       appender.close();
419       String[] retval = new String[packets];
420       byte[] buf = new byte[1000];
421       for(int i = 0; i < packets; i++) {
422           DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
423           ds.receive(p);
424           retval[i] = new String(p.getData(), 0, p.getLength());
425       }
426       ds.close();
427       return retval;
428     }
429 
430     public void testActualLogging() throws Exception {
431       String s = log(false, "greetings", null, 1)[0];
432       StringTokenizer st = new StringTokenizer(s, "<>() ");
433       assertEquals("14", st.nextToken());
434       assertEquals("greetings", st.nextToken());
435     }
436 
437     /**
438      * Exception with printStackTrace that breaks earlier SyslogAppender.
439      */
440     private static class MishandledException extends Exception {
441         private static final long serialVersionUID = 1L;
442         /*
443          *   Create new instance.
444          */
445         public MishandledException() {
446         }
447 
448         /**
449          * Print stack trace.
450          * @param w print writer, may not be null.
451          */
452         public void printStackTrace(final java.io.PrintWriter w) {
453              w.println("Mishandled stack trace follows:");
454              w.println("");
455              w.println("No tab here");
456              w.println("\ttab here");
457              w.println("\t");
458         }
459     }
460 
461     /**
462      * Tests fix for bug 40502.
463      * @throws Exception on IOException.
464      */
465     public void testBadTabbing() throws Exception {
466         String[] s = log(false, "greetings", new MishandledException(), 6);
467         StringTokenizer st = new StringTokenizer(s[0], "<>() ");
468         assertEquals("11", st.nextToken());
469         assertEquals("greetings", st.nextToken());
470         assertEquals("<11>Mishandled stack trace follows:", s[1]);
471         assertEquals("<11>", s[2]);
472         assertEquals("<11>No tab here", s[3]);
473         assertEquals("<11>" + SyslogAppender.TAB + "tab here", s[4]);
474         assertEquals("<11>" + SyslogAppender.TAB, s[5]);
475     }
476 
477     /**
478      * Tests presence of timestamp if header = true.
479      *
480      * @throws Exception if IOException.
481      */
482     public void testHeaderLogging() throws Exception {
483       Date preDate = new Date();
484       String s = log(true, "greetings", null, 1)[0];
485       Date postDate = new Date();
486       assertEquals("<14>", s.substring(0, 4));
487 
488       String syslogDateStr = s.substring(4, 20);
489       SimpleDateFormat fmt = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH);
490       Date syslogDate = fmt.parse(syslogDateStr);
491       Calendar cal = Calendar.getInstance(Locale.ENGLISH);
492       cal.setTime(syslogDate);
493       int syslogMonth = cal.get(Calendar.MONTH);
494       int syslogDay = cal.get(Calendar.DATE);
495       if (syslogDay < 10) {
496           assertEquals(' ', syslogDateStr.charAt(4));
497       }
498       cal.setTime(preDate);
499       int preMonth = cal.get(Calendar.MONTH);
500       cal.set(Calendar.MILLISECOND, 0);
501       preDate = cal.getTime();
502       int syslogYear;
503       if (preMonth == syslogMonth) {
504           syslogYear = cal.get(Calendar.YEAR);
505       } else {
506           cal.setTime(postDate);
507           syslogYear = cal.get(Calendar.YEAR);
508       }
509       cal.setTime(syslogDate);
510       cal.set(Calendar.YEAR, syslogYear);
511       syslogDate = cal.getTime();
512       assertTrue(syslogDate.compareTo(preDate) >= 0);
513       assertTrue(syslogDate.compareTo(postDate) <= 0);
514     }
515 
516 
517     /**
518      * Tests that any header or footer in layout is sent.
519      * @throws Exception if exception during test.
520      */
521     public void testLayoutHeader() throws Exception {
522         DatagramSocket ds = new DatagramSocket();
523         ds.setSoTimeout(2000);
524 
525       SyslogAppender appender = new SyslogAppender();
526       appender.setSyslogHost("localhost:" + ds.getLocalPort());
527       appender.setName("name");
528       appender.setHeader(false);
529       HTMLLayout pl = new HTMLLayout();
530       appender.setLayout(pl);
531       appender.activateOptions();
532 
533       Logger l = Logger.getRootLogger();
534       l.addAppender(appender);
535       l.info("Hello, World");
536       appender.close();
537       String[] s = new String[3];
538       byte[] buf = new byte[1000];
539       for(int i = 0; i < 3; i++) {
540           DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
541           ds.receive(p);
542           s[i] = new String(p.getData(), 0, p.getLength());
543       }
544       ds.close();
545       assertEquals("<14><!DOCTYPE", s[0].substring(0,13));
546       assertEquals("<14></table>", s[2].substring(0,12));
547     }
548 
549     /**
550      * Tests that syslog packets do not exceed 1024 bytes.
551      * See bug 42087.
552      * @throws Exception if exception during test.
553      */
554     public void testBigPackets() throws Exception {
555         DatagramSocket ds = new DatagramSocket();
556         ds.setSoTimeout(2000);
557 
558       SyslogAppender appender = new SyslogAppender();
559       appender.setSyslogHost("localhost:" + ds.getLocalPort());
560       appender.setName("name");
561       appender.setHeader(false);
562       PatternLayout pl = new PatternLayout("%m");
563       appender.setLayout(pl);
564       appender.activateOptions();
565 
566       Logger l = Logger.getRootLogger();
567       l.addAppender(appender);
568       StringBuffer msgbuf = new StringBuffer();
569       while(msgbuf.length() < 8000) {
570           msgbuf.append("0123456789");
571       }
572       String msg = msgbuf.toString();
573       l.info(msg);
574       appender.close();
575       String[] s = new String[8];
576       byte[] buf = new byte[1200];
577       for(int i = 0; i < 8; i++) {
578           DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
579           ds.receive(p);
580           assertTrue(p.getLength() <= 1024);
581           s[i] = new String(p.getData(), 0, p.getLength());
582       }
583       ds.close();
584       StringBuffer rcvbuf = new StringBuffer(s[0]);
585       rcvbuf.delete(0, 4);
586       for(int i = 1; i < 8; i++) {
587           rcvbuf.setLength(rcvbuf.length() - 3);
588           rcvbuf.append(s[i].substring(s[i].indexOf("...") + 3));
589       }
590       assertEquals(msg.length(), rcvbuf.length());
591       assertEquals(msg, rcvbuf.toString());
592     }
593 
594 }