1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.rolling;
18
19 import java.text.SimpleDateFormat;
20 import java.util.ArrayList;
21 import java.util.Calendar;
22 import java.util.Date;
23 import java.util.List;
24
25 import org.apache.logging.log4j.Logger;
26 import org.apache.logging.log4j.core.LogEvent;
27 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
28 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
29 import org.apache.logging.log4j.core.pattern.ArrayPatternConverter;
30 import org.apache.logging.log4j.core.pattern.DatePatternConverter;
31 import org.apache.logging.log4j.core.pattern.FormattingInfo;
32 import org.apache.logging.log4j.core.pattern.PatternConverter;
33 import org.apache.logging.log4j.core.pattern.PatternParser;
34 import org.apache.logging.log4j.status.StatusLogger;
35
36
37
38
39 public class PatternProcessor {
40
41 protected static final Logger LOGGER = StatusLogger.getLogger();
42 private static final String KEY = "FileConverter";
43
44 private static final char YEAR_CHAR = 'y';
45 private static final char MONTH_CHAR = 'M';
46 private static final char[] WEEK_CHARS = {'w', 'W'};
47 private static final char[] DAY_CHARS = {'D', 'd', 'F', 'E'};
48 private static final char[] HOUR_CHARS = {'H', 'K', 'h', 'k'};
49 private static final char MINUTE_CHAR = 'm';
50 private static final char SECOND_CHAR = 's';
51 private static final char MILLIS_CHAR = 'S';
52
53 private final ArrayPatternConverter[] patternConverters;
54 private final FormattingInfo[] patternFields;
55 private final FileExtension fileExtension;
56
57 private long prevFileTime = 0;
58 private long nextFileTime = 0;
59 private long currentFileTime = 0;
60
61 private boolean isTimeBased = false;
62
63 private RolloverFrequency frequency = null;
64
65 private final String pattern;
66
67 public String getPattern() {
68 return pattern;
69 }
70
71 @Override
72 public String toString() {
73 return pattern;
74 }
75
76
77
78
79
80 public PatternProcessor(final String pattern) {
81 this.pattern = pattern;
82 final PatternParser parser = createPatternParser();
83 final List<PatternConverter> converters = new ArrayList<>();
84 final List<FormattingInfo> fields = new ArrayList<>();
85 parser.parse(pattern, converters, fields, false, false, false);
86 final FormattingInfo[] infoArray = new FormattingInfo[fields.size()];
87 patternFields = fields.toArray(infoArray);
88 final ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()];
89 patternConverters = converters.toArray(converterArray);
90 this.fileExtension = FileExtension.lookupForFile(pattern);
91
92 for (final ArrayPatternConverter converter : patternConverters) {
93 if (converter instanceof DatePatternConverter) {
94 final DatePatternConverter dateConverter = (DatePatternConverter) converter;
95 frequency = calculateFrequency(dateConverter.getPattern());
96 }
97 }
98 }
99
100
101
102
103
104
105
106 public PatternProcessor(final String pattern, final PatternProcessor copy) {
107 this(pattern);
108 this.prevFileTime = copy.prevFileTime;
109 this.nextFileTime = copy.nextFileTime;
110 this.currentFileTime = copy.currentFileTime;
111 }
112
113 public void setTimeBased(boolean isTimeBased) {
114 this.isTimeBased = isTimeBased;
115 }
116
117 public long getCurrentFileTime() {
118 return currentFileTime;
119 }
120
121 public void setCurrentFileTime(final long currentFileTime) {
122 this.currentFileTime = currentFileTime;
123 }
124
125 public long getPrevFileTime() {
126 return prevFileTime;
127 }
128
129 public void setPrevFileTime(final long prevFileTime) {
130 LOGGER.debug("Setting prev file time to {}", new Date(prevFileTime));
131 this.prevFileTime = prevFileTime;
132 }
133
134 public FileExtension getFileExtension() {
135 return fileExtension;
136 }
137
138
139
140
141
142
143
144
145 public long getNextTime(final long currentMillis, final int increment, final boolean modulus) {
146
147
148
149
150 prevFileTime = nextFileTime;
151 long nextTime;
152
153 if (frequency == null) {
154 throw new IllegalStateException("Pattern does not contain a date");
155 }
156 final Calendar currentCal = Calendar.getInstance();
157 currentCal.setTimeInMillis(currentMillis);
158 final Calendar cal = Calendar.getInstance();
159 currentCal.setMinimalDaysInFirstWeek(7);
160 cal.setMinimalDaysInFirstWeek(7);
161 cal.set(currentCal.get(Calendar.YEAR), 0, 1, 0, 0, 0);
162 cal.set(Calendar.MILLISECOND, 0);
163 if (frequency == RolloverFrequency.ANNUALLY) {
164 increment(cal, Calendar.YEAR, increment, modulus);
165 nextTime = cal.getTimeInMillis();
166 cal.add(Calendar.YEAR, -1);
167 nextFileTime = cal.getTimeInMillis();
168 return debugGetNextTime(nextTime);
169 }
170 cal.set(Calendar.MONTH, currentCal.get(Calendar.MONTH));
171 if (frequency == RolloverFrequency.MONTHLY) {
172 increment(cal, Calendar.MONTH, increment, modulus);
173 nextTime = cal.getTimeInMillis();
174 cal.add(Calendar.MONTH, -1);
175 nextFileTime = cal.getTimeInMillis();
176 return debugGetNextTime(nextTime);
177 }
178 if (frequency == RolloverFrequency.WEEKLY) {
179 cal.set(Calendar.WEEK_OF_YEAR, currentCal.get(Calendar.WEEK_OF_YEAR));
180 increment(cal, Calendar.WEEK_OF_YEAR, increment, modulus);
181 cal.set(Calendar.DAY_OF_WEEK, currentCal.getFirstDayOfWeek());
182 nextTime = cal.getTimeInMillis();
183 cal.add(Calendar.WEEK_OF_YEAR, -1);
184 nextFileTime = cal.getTimeInMillis();
185 return debugGetNextTime(nextTime);
186 }
187 cal.set(Calendar.DAY_OF_YEAR, currentCal.get(Calendar.DAY_OF_YEAR));
188 if (frequency == RolloverFrequency.DAILY) {
189 increment(cal, Calendar.DAY_OF_YEAR, increment, modulus);
190 nextTime = cal.getTimeInMillis();
191 cal.add(Calendar.DAY_OF_YEAR, -1);
192 nextFileTime = cal.getTimeInMillis();
193 return debugGetNextTime(nextTime);
194 }
195 cal.set(Calendar.HOUR_OF_DAY, currentCal.get(Calendar.HOUR_OF_DAY));
196 if (frequency == RolloverFrequency.HOURLY) {
197 increment(cal, Calendar.HOUR_OF_DAY, increment, modulus);
198 nextTime = cal.getTimeInMillis();
199 cal.add(Calendar.HOUR_OF_DAY, -1);
200 nextFileTime = cal.getTimeInMillis();
201 return debugGetNextTime(nextTime);
202 }
203 cal.set(Calendar.MINUTE, currentCal.get(Calendar.MINUTE));
204 if (frequency == RolloverFrequency.EVERY_MINUTE) {
205 increment(cal, Calendar.MINUTE, increment, modulus);
206 nextTime = cal.getTimeInMillis();
207 cal.add(Calendar.MINUTE, -1);
208 nextFileTime = cal.getTimeInMillis();
209 return debugGetNextTime(nextTime);
210 }
211 cal.set(Calendar.SECOND, currentCal.get(Calendar.SECOND));
212 if (frequency == RolloverFrequency.EVERY_SECOND) {
213 increment(cal, Calendar.SECOND, increment, modulus);
214 nextTime = cal.getTimeInMillis();
215 cal.add(Calendar.SECOND, -1);
216 nextFileTime = cal.getTimeInMillis();
217 return debugGetNextTime(nextTime);
218 }
219 cal.set(Calendar.MILLISECOND, currentCal.get(Calendar.MILLISECOND));
220 increment(cal, Calendar.MILLISECOND, increment, modulus);
221 nextTime = cal.getTimeInMillis();
222 cal.add(Calendar.MILLISECOND, -1);
223 nextFileTime = cal.getTimeInMillis();
224 return debugGetNextTime(nextTime);
225 }
226
227 public void updateTime() {
228 if (nextFileTime != 0 || !isTimeBased) {
229 prevFileTime = nextFileTime;
230 }
231 }
232
233 private long debugGetNextTime(final long nextTime) {
234 if (LOGGER.isTraceEnabled()) {
235 LOGGER.trace("PatternProcessor.getNextTime returning {}, nextFileTime={}, prevFileTime={}, current={}, freq={}",
236 format(nextTime), format(nextFileTime), format(prevFileTime), format(System.currentTimeMillis()), frequency);
237 }
238 return nextTime;
239 }
240
241 private String format(final long time) {
242 return new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss.SSS").format(new Date(time));
243 }
244
245 private void increment(final Calendar cal, final int type, final int increment, final boolean modulate) {
246 final int interval = modulate ? increment - (cal.get(type) % increment) : increment;
247 cal.add(type, interval);
248 }
249
250
251
252
253
254
255 public final void formatFileName(final StringBuilder buf, final boolean useCurrentTime, final Object obj) {
256 long time = useCurrentTime ? currentFileTime : prevFileTime;
257 if (time == 0) {
258 time = System.currentTimeMillis();
259 }
260 formatFileName(buf, new Date(time), obj);
261 }
262
263
264
265
266
267
268
269 public final void formatFileName(final StrSubstitutor subst, final StringBuilder buf, final Object obj) {
270 formatFileName(subst, buf, false, obj);
271 }
272
273
274
275
276
277
278
279 public final void formatFileName(final StrSubstitutor subst, final StringBuilder buf, final boolean useCurrentTime,
280 final Object obj) {
281
282
283 LOGGER.debug("Formatting file name. useCurrentTime={}. currentFileTime={}, prevFileTime={}",
284 useCurrentTime, currentFileTime, prevFileTime);
285 final long time = useCurrentTime ? currentFileTime != 0 ? currentFileTime : System.currentTimeMillis() :
286 prevFileTime != 0 ? prevFileTime : System.currentTimeMillis();
287 formatFileName(buf, new Date(time), obj);
288 final LogEvent event = new Log4jLogEvent.Builder().setTimeMillis(time).build();
289 final String fileName = subst.replace(event, buf);
290 buf.setLength(0);
291 buf.append(fileName);
292 }
293
294
295
296
297
298
299 protected final void formatFileName(final StringBuilder buf, final Object... objects) {
300 for (int i = 0; i < patternConverters.length; i++) {
301 final int fieldStart = buf.length();
302 patternConverters[i].format(buf, objects);
303
304 if (patternFields[i] != null) {
305 patternFields[i].format(fieldStart, buf);
306 }
307 }
308 }
309
310 private RolloverFrequency calculateFrequency(final String pattern) {
311 if (patternContains(pattern, MILLIS_CHAR)) {
312 return RolloverFrequency.EVERY_MILLISECOND;
313 }
314 if (patternContains(pattern, SECOND_CHAR)) {
315 return RolloverFrequency.EVERY_SECOND;
316 }
317 if (patternContains(pattern, MINUTE_CHAR)) {
318 return RolloverFrequency.EVERY_MINUTE;
319 }
320 if (patternContains(pattern, HOUR_CHARS)) {
321 return RolloverFrequency.HOURLY;
322 }
323 if (patternContains(pattern, DAY_CHARS)) {
324 return RolloverFrequency.DAILY;
325 }
326 if (patternContains(pattern, WEEK_CHARS)) {
327 return RolloverFrequency.WEEKLY;
328 }
329 if (patternContains(pattern, MONTH_CHAR)) {
330 return RolloverFrequency.MONTHLY;
331 }
332 if (patternContains(pattern, YEAR_CHAR)) {
333 return RolloverFrequency.ANNUALLY;
334 }
335 return null;
336 }
337
338 private PatternParser createPatternParser() {
339
340 return new PatternParser(null, KEY, null);
341 }
342
343 private boolean patternContains(final String pattern, final char... chars) {
344 for (final char character : chars) {
345 if (patternContains(pattern, character)) {
346 return true;
347 }
348 }
349 return false;
350 }
351
352 private boolean patternContains(final String pattern, final char character) {
353 return pattern.indexOf(character) >= 0;
354 }
355
356 public RolloverFrequency getFrequency() {
357 return frequency;
358 }
359
360 public long getNextFileTime() {
361 return nextFileTime;
362 }
363
364 }