View Javadoc

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  package org.apache.logging.log4j.core.appender.rolling;
18  
19  import org.apache.logging.log4j.core.pattern.ArrayPatternConverter;
20  import org.apache.logging.log4j.core.pattern.DatePatternConverter;
21  import org.apache.logging.log4j.core.pattern.FormattingInfo;
22  import org.apache.logging.log4j.core.pattern.PatternConverter;
23  import org.apache.logging.log4j.core.pattern.PatternParser;
24  
25  import java.util.ArrayList;
26  import java.util.Calendar;
27  import java.util.Date;
28  import java.util.List;
29  
30  /**
31   * Parse the rollover pattern.
32   */
33  public class PatternProcessor {
34  
35      private static final String KEY = "FileConverter";
36  
37      private static final char YEAR_CHAR = 'y';
38      private static final char MONTH_CHAR = 'M';
39      private static final char[] WEEK_CHARS = {'w', 'W'};
40      private static final char[] DAY_CHARS = {'D', 'd', 'F', 'E'};
41      private static final char[] HOUR_CHARS = {'H', 'K', 'h', 'k'};
42      private static final char MINUTE_CHAR = 'm';
43      private static final char SECOND_CHAR = 's';
44      private static final char MILLIS_CHAR = 'S';
45  
46      private final ArrayPatternConverter[] patternConverters;
47      private final FormattingInfo[] patternFields;
48  
49      private RolloverFrequency frequency = null;
50  
51      /**
52       * Constructor.
53       * @param pattern The file pattern.
54       */
55      public PatternProcessor(String pattern) {
56          PatternParser parser = createPatternParser();
57          List<PatternConverter> converters = new ArrayList<PatternConverter>();
58          List<FormattingInfo> fields = new ArrayList<FormattingInfo>();
59          parser.parse(pattern, converters, fields);
60          FormattingInfo[] infoArray = new FormattingInfo[fields.size()];
61          patternFields = fields.toArray(infoArray);
62          ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()];
63          patternConverters = converters.toArray(converterArray);
64  
65          for (ArrayPatternConverter converter : patternConverters) {
66              if (converter instanceof DatePatternConverter) {
67                  DatePatternConverter dateConverter = (DatePatternConverter) converter;
68                  frequency = calculateFrequency(dateConverter.getPattern());
69              }
70          }
71      }
72  
73      /**
74       * Returns the next potential rollover time.
75       * @param current The current time.
76       * @param increment The increment to the next time.
77       * @return the next potential rollover time.
78       */
79      public long getNextTime(long current, int increment, boolean modulus) {
80          if (frequency == null) {
81              throw new IllegalStateException("Pattern does not contain a date");
82          }
83          Calendar currentCal = Calendar.getInstance();
84          currentCal.setTimeInMillis(current);
85          Calendar cal = Calendar.getInstance();
86          cal.set(currentCal.get(Calendar.YEAR), 0, 1, 0, 0, 0);
87          cal.set(Calendar.MILLISECOND, 0);
88          if (frequency == RolloverFrequency.ANNUALLY) {
89              increment(cal, Calendar.YEAR, increment, modulus);
90              return cal.getTimeInMillis();
91          }
92          if (frequency == RolloverFrequency.MONTHLY) {
93              increment(cal, Calendar.MONTH, increment, modulus);
94              return cal.getTimeInMillis();
95          }
96          if (frequency == RolloverFrequency.WEEKLY) {
97              increment(cal, Calendar.WEEK_OF_YEAR, increment, modulus);
98              return cal.getTimeInMillis();
99          }
100         cal.set(Calendar.DAY_OF_YEAR, currentCal.get(Calendar.DAY_OF_YEAR));
101         if (frequency == RolloverFrequency.DAILY) {
102             increment(cal, Calendar.DAY_OF_YEAR, increment, modulus);
103             return cal.getTimeInMillis();
104         }
105         cal.set(Calendar.HOUR, currentCal.get(Calendar.HOUR));
106         if (frequency == RolloverFrequency.HOURLY) {
107             increment(cal, Calendar.HOUR, increment, modulus);
108             return cal.getTimeInMillis();
109         }
110         cal.set(Calendar.MINUTE, currentCal.get(Calendar.MINUTE));
111         if (frequency == RolloverFrequency.EVERY_MINUTE) {
112             increment(cal, Calendar.MINUTE, increment, modulus);
113             return cal.getTimeInMillis();
114         }
115         cal.set(Calendar.SECOND, currentCal.get(Calendar.SECOND));
116         if (frequency == RolloverFrequency.EVERY_SECOND) {
117             increment(cal, Calendar.SECOND, increment, modulus);
118             return cal.getTimeInMillis();
119         }
120         increment(cal, Calendar.MILLISECOND, increment, modulus);
121         return cal.getTimeInMillis();
122     }
123 
124     private void increment(Calendar cal, int type, int increment, boolean modulate) {
125         int interval =  modulate ? increment - (cal.get(type) % increment) : increment;
126         cal.add(type, interval);
127     }
128 
129     /**
130      * Format file name.
131      * @param buf string buffer to which formatted file name is appended, may not be null.
132      * @param obj object to be evaluated in formatting, may not be null.
133      */
134     protected final void formatFileName(final StringBuilder buf, final Object obj) {
135         formatFileName(buf, new Date(System.currentTimeMillis()), obj);
136     }
137 
138     /**
139      * Format file name.
140      * @param buf string buffer to which formatted file name is appended, may not be null.
141      * @param objects objects to be evaluated in formatting, may not be null.
142      */
143     protected final void formatFileName(final StringBuilder buf, final Object... objects) {
144         for (int i = 0; i < patternConverters.length; i++) {
145             int fieldStart = buf.length();
146             patternConverters[i].format(buf, objects);
147 
148             if (patternFields[i] != null) {
149                 patternFields[i].format(fieldStart, buf);
150             }
151         }
152     }
153 
154     private RolloverFrequency calculateFrequency(String pattern) {
155         if (patternContains(pattern, MILLIS_CHAR)) {
156             return RolloverFrequency.EVERY_MILLISECOND;
157         }
158         if (patternContains(pattern, SECOND_CHAR)) {
159             return RolloverFrequency.EVERY_SECOND;
160         }
161         if (patternContains(pattern, MINUTE_CHAR)) {
162             return RolloverFrequency.EVERY_MINUTE;
163         }
164         if (patternContains(pattern, HOUR_CHARS)) {
165             return RolloverFrequency.HOURLY;
166         }
167         if (patternContains(pattern, DAY_CHARS)) {
168             return RolloverFrequency.DAILY;
169         }
170         if (patternContains(pattern, WEEK_CHARS)) {
171             return RolloverFrequency.WEEKLY;
172         }
173         if (patternContains(pattern, MONTH_CHAR)) {
174             return RolloverFrequency.MONTHLY;
175         }
176         if (patternContains(pattern, YEAR_CHAR)) {
177             return RolloverFrequency.ANNUALLY;
178         }
179         return null;
180     }
181 
182     private PatternParser createPatternParser() {
183 
184         return new PatternParser(null, KEY, null);
185     }
186 
187     private boolean patternContains(String pattern, char... chars) {
188         for (char character : chars) {
189             if (patternContains(pattern, character)) {
190                 return true;
191             }
192         }
193         return false;
194     }
195 
196     private boolean patternContains(String pattern, char character) {
197         return pattern.indexOf(character) >= 0;
198     }
199 }