    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 }