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