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