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