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