    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
    22  import java.io.File;
    23  import java.io.FileOutputStream;
    24  import java.io.IOException;
    25  import java.io.FileInputStream;
    26  import java.text.SimpleDateFormat;
    27  import java.util.Calendar;
    28  import java.util.Date;
    29  import org.apache.log4j.util.Compare;
    30
    31  /**
    32     Exhaustive test of the DailyRollingFileAppender compute algorithm.
    33
    34     @author Ceki G&uuml;lc&uuml;
    35     @author Curt Arnold
    36   */
    37  public class DRFATestCase extends TestCase {
    38
    39      /**
    40       * Create new test.
    41       * @param name test name.
    42       */
    43    public DRFATestCase(final String name) {
    44      super(name);
    45    }
    46
    47      /**
    48       * Reset configuration after every test.
    49       */
    50    public void tearDown() {
    51        LogManager.resetConfiguration();
    52    }
    53
    54      /**
    55       * Test prediction of check period.
    56       */
    57    public
    58    void testComputeCheckPeriod() {
    59      DailyRollingFileAppender drfa = new DailyRollingFileAppender();
    60      drfa.setName("testComputeCheckPeriod");
    61      drfa.setDatePattern("yyyy-MM-dd.'log'");
    62      drfa.activateOptions();
    63
    64      drfa.computeCheckPeriod();
    65      assertEquals(drfa.computeCheckPeriod(),
    66           DailyRollingFileAppender.TOP_OF_DAY);
    67
    68      drfa.setDatePattern("yyyy-MM-dd mm.'log'");
    69      assertEquals(drfa.computeCheckPeriod(),
    70           DailyRollingFileAppender.TOP_OF_MINUTE);
    71
    72      drfa.setDatePattern("yyyy-MM-dd a.'log'");
    73      assertEquals(drfa.computeCheckPeriod(),
    74           DailyRollingFileAppender.HALF_DAY);
    75
    76      drfa.setDatePattern("yyyy-MM-dd HH.'log'");
    77      assertEquals(drfa.computeCheckPeriod(),
    78           DailyRollingFileAppender.TOP_OF_HOUR);
    79
    80      drfa.setDatePattern("yyyy-MM.'log'");
    81      assertEquals(drfa.computeCheckPeriod(),
    82           DailyRollingFileAppender.TOP_OF_MONTH);
    83
    84      drfa.setDatePattern("'log'HH'log'");
    85      assertEquals(drfa.computeCheckPeriod(),
    86           DailyRollingFileAppender.TOP_OF_HOUR);
    87    }
    88
    89
    90      /**
    91       *   Test of RollingCalendar.
    92       */
    93    public
    94    void testRC1() {
    95      RollingCalendar rc = new RollingCalendar();
    96      rc.setType(DailyRollingFileAppender.TOP_OF_DAY);
    97
    98      Calendar c = Calendar.getInstance();
    99
    100     // jan, mar, may, july, aug, oct, dec have 31 days
    101     int [] M31 = {0,2,4,6,7,9,11};
    102
    103     for(int i = 0; i < M31.length; i ++) {
    104       for(int d = 1; d <=31; d++) {
    105     for(int h = 0; h < 23; h++) {
    106       c.clear();
    107       c.set(Calendar.YEAR, 20);
    108       c.set(Calendar.MONTH, Calendar.JANUARY + M31[i]);
    109       c.set(Calendar.DAY_OF_MONTH, d);
    110       c.set(Calendar.HOUR_OF_DAY, h);
    111       c.set(Calendar.MINUTE, 10);
    112       c.set(Calendar.SECOND, 10);
    113       c.set(Calendar.MILLISECOND, 88);
    114
    115       c.setTime(rc.getNextCheckDate(c.getTime()));
    116       if(d == 31) {
    117         assertEquals(c.get(Calendar.MONTH),(Calendar.JANUARY+M31[i]+1)%12);
    118         assertEquals(c.get(Calendar.DAY_OF_MONTH), 1);
    119       } else {
    120         assertEquals(c.get(Calendar.MONTH), Calendar.JANUARY+M31[i]);
    121         assertEquals(c.get(Calendar.DAY_OF_MONTH), d+1);
    122       }
    123       assertEquals(c.get(Calendar.HOUR_OF_DAY), 0);
    124       assertEquals(c.get(Calendar.MINUTE), 0);
    125       assertEquals(c.get(Calendar.SECOND), 0);
    126       assertEquals(c.get(Calendar.MILLISECOND), 0);
    127     }
    128       }
    129     }
    130   }
    131
    132     /**
    133      * RollingCalendar test.
    134      */
    135   public
    136   void testRC2() {
    137     RollingCalendar rc = new RollingCalendar();
    138
    139     rc.setType(DailyRollingFileAppender.TOP_OF_HOUR);
    140
    141     Calendar c = Calendar.getInstance();
    142
    143     // jan, mar, may, july, aug, oct, dec have 31 days
    144     int [] M31 = {0,2,4,6,7,9,11};
    145
    146     for(int i = 0; i < M31.length; i ++) {
    147       System.out.println("Month = "+(M31[i]+1));
    148       for(int d = 1; d <= 31; d++) {
    149     for(int h = 0; h < 23; h++) {
    150       for(int m = 0; m <= 59; m++) {
    151         c.clear();
    152         c.set(Calendar.YEAR, 20);
    153         c.set(Calendar.MONTH, Calendar.JANUARY + M31[i]);
    154         c.set(Calendar.DAY_OF_MONTH, d);
    155         c.set(Calendar.HOUR_OF_DAY, h);
    156         c.set(Calendar.MINUTE, m);
    157         c.set(Calendar.SECOND, 12);
    158         c.set(Calendar.MILLISECOND, 88);
    159
    160         boolean dltState0 = c.getTimeZone().inDaylightTime(c.getTime());
    161         c.setTime(rc.getNextCheckDate(c.getTime()));
    162         boolean dltState1 = c.getTimeZone().inDaylightTime(c.getTime());
    163
    164         assertEquals(c.get(Calendar.MILLISECOND), 0);
    165         assertEquals(c.get(Calendar.SECOND), 0);
    166         assertEquals(c.get(Calendar.MINUTE), 0);
    167
    168         if(dltState0 == dltState1) {
    169           assertEquals(c.get(Calendar.HOUR_OF_DAY), (h+1)%24);
    170         } else {
    171           // returning to standard time
    172           if(dltState0) {
    173         assertEquals(c.get(Calendar.HOUR_OF_DAY), h);
    174           } else { // switching to day light saving time
    175         //System.err.println("m="+m+", h="+h+", d="+d+", i="+i);
    176         //if(h==2) {
    177         // System.err.println(c);
    178         //}
    179         //assertEquals(c.get(Calendar.HOUR_OF_DAY), (h+2)%24);
    180           }
    181         }
    182
    183         if(h == 23) {
    184           assertEquals(c.get(Calendar.DAY_OF_MONTH), (d+1)%32);
    185           if(d == 31) {
    186         assertEquals(c.get(Calendar.MONTH),
    187                  (Calendar.JANUARY+M31[i]+1)%12);
    188           } else {
    189         assertEquals(c.get(Calendar.MONTH),
    190                  Calendar.JANUARY+M31[i]);
    191           }
    192         } else {
    193           assertEquals(c.get(Calendar.DAY_OF_MONTH), d);
    194           assertEquals(c.get(Calendar.MONTH), Calendar.JANUARY+M31[i]);
    195         }
    196       }
    197     }
    198       }
    199     }
    200   }
    201
    202     /**
    203      * RollingCalendar test.
    204      */
    205   public
    206   void testRC3() {
    207     RollingCalendar rc = new RollingCalendar();
    208
    209     rc.setType(DailyRollingFileAppender.TOP_OF_MINUTE);
    210
    211     int[] S = {0, 1, 5, 10, 21, 30, 59};
    212     int[] M = {0, 1, 5, 10, 21, 30, 59};
    213     Calendar c = Calendar.getInstance();
    214
    215     // jan, mar, may, july, aug, oct, dec have 31 days
    216     int [] M31 = {2,9,0,4,6,7,11};
    217
    218     for(int i = 0; i < M31.length; i ++) {
    219       System.out.println("Month = "+(M31[i]+1));
    220       for(int d = 1; d <= 31; d++) {
    221     for(int h = 0; h < 23; h++) {
    222       for(int m = 0; m < M.length; m++) {
    223         for(int s = 0; s < S.length; s++) {
    224           c.clear();
    225           c.set(Calendar.YEAR, 20);
    226           c.set(Calendar.MONTH, Calendar.JANUARY + M31[i]);
    227           c.set(Calendar.DAY_OF_MONTH, d);
    228           c.set(Calendar.HOUR_OF_DAY, h);
    229           c.set(Calendar.MINUTE, M[m]);
    230           c.set(Calendar.SECOND, S[s]);
    231           c.set(Calendar.MILLISECOND, 88);
    232           c.add(Calendar.MILLISECOND, 1);
    233
    234           boolean dltState0 = c.getTimeZone().inDaylightTime(c.getTime());
    235
    236           c.setTime(rc.getNextCheckDate(c.getTime()));
    237           c.add(Calendar.MILLISECOND, 0);
    238           boolean dltState1 = c.getTimeZone().inDaylightTime(c.getTime());
    239
    240           assertEquals(c.get(Calendar.MILLISECOND), 0);
    241           assertEquals(c.get(Calendar.SECOND), 0);
    242           assertEquals(c.get(Calendar.MINUTE), (M[m]+1)%60);
    243
    244           if(M[m] == 59) {
    245         if(dltState0 == dltState1) {
    246           assertEquals(c.get(Calendar.HOUR_OF_DAY), (h+1)%24);
    247         }
    248         if(h == 23) {
    249           assertEquals(c.get(Calendar.DAY_OF_MONTH), (d+1)%32);
    250           if(d == 31) {
    251               assertEquals(c.get(Calendar.MONTH),
    252                  (Calendar.JANUARY+M31[i]+1)%12);
    253           } else {
    254             assertEquals(c.get(Calendar.MONTH),
    255                  Calendar.JANUARY+M31[i]);
    256           }
    257         } else {
    258           assertEquals(c.get(Calendar.DAY_OF_MONTH), d);
    259         }
    260           } else {
    261         // allow discrepancies only if we are switching from std to dls time
    262         if(c.get(Calendar.HOUR_OF_DAY) != h) {
    263           c.add(Calendar.HOUR_OF_DAY, +1);
    264           boolean dltState2 = c.getTimeZone().inDaylightTime(c.getTime());
    265           if(dltState1 == dltState2) {
    266             fail("No switch");
    267           }
    268         }
    269         assertEquals(c.get(Calendar.DAY_OF_MONTH), d);
    270         assertEquals(c.get(Calendar.MONTH), Calendar.JANUARY+M31[i]);
    271           }
    272         }
    273       }
    274     }
    275       }
    276     }
    277   }
    278
    279
    280     /**
    281      * Common test code for 3 parameter constructor.
    282      *
    283      * @throws IOException if IOException during test.
    284      */
    285    public void test3Param(final String datePattern,
    286                           final String filename) throws IOException {
    287        Layout layout = new SimpleLayout();
    288        DailyRollingFileAppender appender =
    289                new DailyRollingFileAppender(layout, filename, datePattern);
    290        assertEquals(datePattern, appender.getDatePattern());
    291        Logger root = Logger.getRootLogger();
    292        root.addAppender(appender);
    293        root.info("Hello, World");
    294        assertTrue(new File(filename).exists());
    295     }
    296
    297     /**
    298      * Creates an appender with an unrecognized top-of-year pattern.
    299      *
    300      * @throws IOException if IOException during test.
    301      */
    302     public void testTopOfYear() throws IOException {
    303         try {
    304             test3Param("'.'yyyy", "output/drfa_topOfYear.log");
    305             fail("Expected illegal state exception.");
    306         } catch(IllegalStateException ex) {
    307             assertNotNull(ex);
    308         }
    309     }
    310
    311     /**
    312      * Creates an appender with a top-of-month pattern.
    313      *
    314      * @throws IOException if IOException during test.
    315      */
    316     public void testTopOfMonth() throws IOException {
    317         test3Param("'.'yyyy-MM", "output/drfa_topOfMonth.log");
    318     }
    319
    320
    321     /**
    322      * Creates an appender with a top-of-week pattern.
    323      *
    324      * @throws IOException if IOException during test.
    325      */
    326     public void testTopOfWeek() throws IOException {
    327         test3Param("'.'yyyy-w", "output/drfa_topOfWeek.log");
    328     }
    329
    330     /**
    331      * Creates an appender with a top-of-day pattern.
    332      *
    333      * @throws IOException if IOException during test.
    334      */
    335     public void testTopOfDay() throws IOException {
    336         test3Param("'.'yyyy-MM-dd", "output/drfa_topOfDay.log");
    337     }
    338
    339
    340     /**
    341      * Creates an appender with a half day pattern.
    342      *
    343      * @throws IOException if IOException during test.
    344      */
    345     public void testHalfDay() throws IOException {
    346         test3Param("'.'yyyy-MM-dd-a", "output/drfa_halfDay.log");
    347     }
    348
    349     /**
    350      * Creates an appender with a top-of-hour pattern.
    351      *
    352      * @throws IOException if IOException during test.
    353      */
    354     public void testTopOfHour() throws IOException {
    355         test3Param("'.'yyyy-MM-dd-HH", "output/drfa_topOfHour.log");
    356     }
    357
    358     /**
    359      * Creates an appender with a top-of-day pattern.
    360      *
    361      * @throws IOException if IOException during test.
    362      */
    363     public void testTopOfMinute() throws IOException {
    364         test3Param("'.'yyyy-MM-dd-HH-mm", "output/drfa_topOfMinute.log");
    365     }
    366
    367     /**
    368      * Attempts to rollOver with no date pattern set.
    369      *
    370      * @throws IOException if IOException during test.
    371      */
    372     public void testRolloverNoPattern() throws IOException {
    373         Layout layout = new SimpleLayout();
    374         DailyRollingFileAppender appender =
    375                 new DailyRollingFileAppender(layout, "output/drfa_nopattern.log", null);
    376
    377         VectorErrorHandler errorHandler = new VectorErrorHandler();
    378         appender.setErrorHandler(errorHandler);
    379         appender.rollOver();
    380         assertEquals(1, errorHandler.size());
    381         assertEquals("Missing DatePattern option in rollOver().",
    382                 errorHandler.getMessage(0));
    383     }
    384
    385     /**
    386      * Tests rollOver with a minute periodicity.
    387      *
    388      * @throws IOException
    389      * @throws InterruptedException
    390      */
    391     public void testMinuteRollover() throws IOException, InterruptedException {
    392         Layout layout = new SimpleLayout();
    393         String filename = "output/drfa_minuteRollover.log";
    394         String pattern = "'.'yyyy-MM-dd-HH-mm";
    395
    396         DailyRollingFileAppender appender =
    397                 new DailyRollingFileAppender(layout,
    398                         filename,
    399                         pattern);
    400         Logger root = Logger.getRootLogger();
    401         root.addAppender(appender);
    402         File firstFile =
    403                 new File(filename + new SimpleDateFormat(pattern).format(new Date()));
    404         root.info("Hello, World");
    405         //
    406         //   create a file by that name so it has to be deleted
    407         //       on rollover
    408         firstFile.createNewFile();
    409         assertTrue(firstFile.exists());
    410         assertEquals(0, firstFile.length());
    411
    412         Calendar cal = Calendar.getInstance();
    413         long now = cal.getTime().getTime();
    414         cal.set(Calendar.SECOND, 3);
    415         cal.set(Calendar.MILLISECOND, 0);
    416         cal.add(Calendar.MINUTE, 1);
    417         long until = cal.getTime().getTime();
    418         Thread.sleep(until - now);
    419         root.info("Hello, World");
    420         assertTrue(firstFile.exists());
    421         assertTrue(firstFile.length() > 0);
    422
    423     }
    424
    425     /**
    426      * Naive append method to combine rollover fragments.
    427      * @param combined stream to which source is appended.
    428      * @param source stream containing bytes to append.
    429      * @param buf byte array to use in transfer.
    430      * @throws IOException if io error during operation.
    431      */
    432     private static void append(final FileOutputStream combined,
    433                                final FileInputStream source,
    434                                final byte[] buf) throws IOException {
    435         int count1 = source.read(buf);
    436         if (count1 > 0) {
    437             combined.write(buf, 0, count1);
    438         }
    439         source.close();
    440     }
    441
    442     /**
    443      * Tests rollOver when log file is unabled to be renamed.
    444      * See bug 43374.
    445      *
    446      * @throws IOException if io error.
    447      * @throws InterruptedException if test interrupted while waiting for the start of the next minute.
    448      */
    449     public void testBlockedRollover() throws IOException, InterruptedException {
    450         Layout layout = new SimpleLayout();
    451         String filename = "output/drfa_blockedRollover.log";
    452         String pattern = "'.'yyyy-MM-dd-HH-mm";
    453
    454
    455         Date start = new Date();
    456         DailyRollingFileAppender appender =
    457                 new DailyRollingFileAppender(layout,
    458                         filename,
    459                         pattern);
    460         appender.setAppend(false);
    461         Logger root = Logger.getRootLogger();
    462         root.addAppender(appender);
    463         //
    464         //   open next two anticipated rollover file names
    465         //
    466         File block1 = new File(filename + new SimpleDateFormat(pattern).format(start));
    467         File block2 = new File(filename + new SimpleDateFormat(pattern).format(
    468                 new Date(start.getTime() + 60000)));
    469         FileOutputStream os1 = new FileOutputStream(block1);
    470         FileOutputStream os2 = new FileOutputStream(block2);
    471         root.info("Prior to rollover");
    472         //
    473         //   sleep until three seconds into next minute
    474         //
    475         Thread.sleep(63000 - (start.getTime() % 60000));
    476         //
    477         //  should trigger failed rollover
    478         //
    479         root.info("Rollover attempt while blocked");
    480         os1.close();
    481         os2.close();
    482         root.info("Message after block removed");
    483         appender.close();
    484         //
    485         //   combine base file and potential rollovers
    486         //      since rollover may or may not have been blocked
    487         //      depending on platform.
    488         //
    489         String combinedFilename = "output/drfa_blockedRollover.combined";
    490         FileOutputStream combined = new FileOutputStream(combinedFilename);
    491         byte[] buf = new byte[500];
    492         append(combined, new FileInputStream(block1), buf);
    493         append(combined, new FileInputStream(block2), buf);
    494         append(combined, new FileInputStream(filename), buf);
    495         combined.close();
    496         assertTrue(Compare.compare(combinedFilename,
    497                 "witness/drfa_blockedRollover.log"));
    498     }
    499
    500     /** Check that the computed rollover period for a pattern containing a week as the finest unit is set to be
    501      * a week.  Due to a locale mismatch this was incorrect in non-English locales.  See bug 40888.
    502      *
    503      */
    504     public void testWeeklyRollover() {
    505         DailyRollingFileAppender drfa = new DailyRollingFileAppender();
    506     	drfa.setDatePattern("'.'yyyy-ww");
    507 		int checkPeriod = drfa.computeCheckPeriod();
    508 		assertEquals(DailyRollingFileAppender.TOP_OF_WEEK, checkPeriod);
    509     }
    510
    511
    512 }