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