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 long prevFileTime = 0;
50      private long nextFileTime = 0;
51  
52      private RolloverFrequency frequency = null;
53  
54      /**
55       * Constructor.
56       * @param pattern The file pattern.
57       */
58      public PatternProcessor(final String pattern) {
59          final PatternParser parser = createPatternParser();
60          final List<PatternConverter> converters = new ArrayList<PatternConverter>();
61          final List<FormattingInfo> fields = new ArrayList<FormattingInfo>();
62          parser.parse(pattern, converters, fields);
63          final FormattingInfo[] infoArray = new FormattingInfo[fields.size()];
64          patternFields = fields.toArray(infoArray);
65          final ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()];
66          patternConverters = converters.toArray(converterArray);
67  
68          for (final ArrayPatternConverter converter : patternConverters) {
69              if (converter instanceof DatePatternConverter) {
70                  final DatePatternConverter dateConverter = (DatePatternConverter) converter;
71                  frequency = calculateFrequency(dateConverter.getPattern());
72              }
73          }
74      }
75  
76      /**
77       * Returns the next potential rollover time.
78       * @param current The current time.
79       * @param increment The increment to the next time.
80       * @param modulus If true the time will be rounded to occur on a boundary aligned with the increment.
81       * @return the next potential rollover time and the timestamp for the target file.
82       */
83      public long getNextTime(final long current, final int increment, final boolean modulus) {
84          prevFileTime = nextFileTime;
85          long nextTime;
86  
87          if (frequency == null) {
88              throw new IllegalStateException("Pattern does not contain a date");
89          }
90          final Calendar currentCal = Calendar.getInstance();
91          currentCal.setTimeInMillis(current);
92          final Calendar cal = Calendar.getInstance();
93          cal.set(currentCal.get(Calendar.YEAR), 0, 1, 0, 0, 0);
94          cal.set(Calendar.MILLISECOND, 0);
95          if (frequency == RolloverFrequency.ANNUALLY) {
96              increment(cal, Calendar.YEAR, increment, modulus);
97              nextTime = cal.getTimeInMillis();
98              cal.add(Calendar.YEAR, -1);
99              nextFileTime = cal.getTimeInMillis();
100             return nextTime;
101         }
102         if (frequency == RolloverFrequency.MONTHLY) {
103             increment(cal, Calendar.MONTH, increment, modulus);
104             nextTime = cal.getTimeInMillis();
105             cal.add(Calendar.MONTH, -1);
106             nextFileTime = cal.getTimeInMillis();
107             return nextTime;
108         }
109         if (frequency == RolloverFrequency.WEEKLY) {
110             increment(cal, Calendar.WEEK_OF_YEAR, increment, modulus);
111             nextTime = cal.getTimeInMillis();
112             cal.add(Calendar.WEEK_OF_YEAR, -1);
113             nextFileTime = cal.getTimeInMillis();
114             return nextTime;
115         }
116         cal.set(Calendar.DAY_OF_YEAR, currentCal.get(Calendar.DAY_OF_YEAR));
117         if (frequency == RolloverFrequency.DAILY) {
118             increment(cal, Calendar.DAY_OF_YEAR, increment, modulus);
119             nextTime = cal.getTimeInMillis();
120             cal.add(Calendar.DAY_OF_YEAR, -1);
121             nextFileTime = cal.getTimeInMillis();
122             return nextTime;
123         }
124         cal.set(Calendar.HOUR, currentCal.get(Calendar.HOUR));
125         if (frequency == RolloverFrequency.HOURLY) {
126             increment(cal, Calendar.HOUR, increment, modulus);
127             nextTime = cal.getTimeInMillis();
128             cal.add(Calendar.HOUR, -1);
129             nextFileTime = cal.getTimeInMillis();
130             return nextTime;
131         }
132         cal.set(Calendar.MINUTE, currentCal.get(Calendar.MINUTE));
133         if (frequency == RolloverFrequency.EVERY_MINUTE) {
134             increment(cal, Calendar.MINUTE, increment, modulus);
135             nextTime = cal.getTimeInMillis();
136             cal.add(Calendar.MINUTE, -1);
137             nextFileTime = cal.getTimeInMillis();
138             return nextTime;
139         }
140         cal.set(Calendar.SECOND, currentCal.get(Calendar.SECOND));
141         if (frequency == RolloverFrequency.EVERY_SECOND) {
142             increment(cal, Calendar.SECOND, increment, modulus);
143             nextTime = cal.getTimeInMillis();
144             cal.add(Calendar.SECOND, -1);
145             nextFileTime = cal.getTimeInMillis();
146             return nextTime;
147         }
148         increment(cal, Calendar.MILLISECOND, increment, modulus);
149         nextTime = cal.getTimeInMillis();
150         cal.add(Calendar.MILLISECOND, -1);
151         nextFileTime = cal.getTimeInMillis();
152         return nextTime;
153     }
154 
155     private void increment(final Calendar cal, final int type, final int increment, final boolean modulate) {
156         final int interval =  modulate ? increment - (cal.get(type) % increment) : increment;
157         cal.add(type, interval);
158     }
159 
160     /**
161      * Format file name.
162      * @param buf string buffer to which formatted file name is appended, may not be null.
163      * @param obj object to be evaluated in formatting, may not be null.
164      */
165     public final void formatFileName(final StringBuilder buf, final Object obj) {
166         final long time = prevFileTime == 0 ? System.currentTimeMillis() : prevFileTime;
167         formatFileName(buf, new Date(time), obj);
168     }
169 
170     /**
171      * Format file name.
172      * @param buf string buffer to which formatted file name is appended, may not be null.
173      * @param objects objects to be evaluated in formatting, may not be null.
174      */
175     protected final void formatFileName(final StringBuilder buf, final Object... objects) {
176         for (int i = 0; i < patternConverters.length; i++) {
177             final int fieldStart = buf.length();
178             patternConverters[i].format(buf, objects);
179 
180             if (patternFields[i] != null) {
181                 patternFields[i].format(fieldStart, buf);
182             }
183         }
184     }
185 
186     private RolloverFrequency calculateFrequency(final String pattern) {
187         if (patternContains(pattern, MILLIS_CHAR)) {
188             return RolloverFrequency.EVERY_MILLISECOND;
189         }
190         if (patternContains(pattern, SECOND_CHAR)) {
191             return RolloverFrequency.EVERY_SECOND;
192         }
193         if (patternContains(pattern, MINUTE_CHAR)) {
194             return RolloverFrequency.EVERY_MINUTE;
195         }
196         if (patternContains(pattern, HOUR_CHARS)) {
197             return RolloverFrequency.HOURLY;
198         }
199         if (patternContains(pattern, DAY_CHARS)) {
200             return RolloverFrequency.DAILY;
201         }
202         if (patternContains(pattern, WEEK_CHARS)) {
203             return RolloverFrequency.WEEKLY;
204         }
205         if (patternContains(pattern, MONTH_CHAR)) {
206             return RolloverFrequency.MONTHLY;
207         }
208         if (patternContains(pattern, YEAR_CHAR)) {
209             return RolloverFrequency.ANNUALLY;
210         }
211         return null;
212     }
213 
214     private PatternParser createPatternParser() {
215 
216         return new PatternParser(null, KEY, null);
217     }
218 
219     private boolean patternContains(final String pattern, final char... chars) {
220         for (final char character : chars) {
221             if (patternContains(pattern, character)) {
222                 return true;
223             }
224         }
225         return false;
226     }
227 
228     private boolean patternContains(final String pattern, final char character) {
229         return pattern.indexOf(character) >= 0;
230     }
231 }