1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.log4j;
21
22 import java.io.IOException;
23 import java.io.File;
24 import java.io.InterruptedIOException;
25 import java.text.SimpleDateFormat;
26 import java.util.Date;
27 import java.util.GregorianCalendar;
28 import java.util.Calendar;
29 import java.util.TimeZone;
30 import java.util.Locale;
31
32 import org.apache.log4j.helpers.LogLog;
33 import org.apache.log4j.spi.LoggingEvent;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 public class DailyRollingFileAppender extends FileAppender {
143
144
145
146
147 static final int TOP_OF_TROUBLE=-1;
148 static final int TOP_OF_MINUTE = 0;
149 static final int TOP_OF_HOUR = 1;
150 static final int HALF_DAY = 2;
151 static final int TOP_OF_DAY = 3;
152 static final int TOP_OF_WEEK = 4;
153 static final int TOP_OF_MONTH = 5;
154
155
156
157
158
159
160 private String datePattern = "'.'yyyy-MM-dd";
161
162
163
164
165
166
167
168
169
170
171
172 private String scheduledFilename;
173
174
175
176 private long nextCheck = System.currentTimeMillis () - 1;
177
178 Date now = new Date();
179
180 SimpleDateFormat sdf;
181
182 RollingCalendar rc = new RollingCalendar();
183
184 int checkPeriod = TOP_OF_TROUBLE;
185
186
187 static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
188
189
190
191
192 public DailyRollingFileAppender() {
193 }
194
195
196
197
198
199
200
201 public DailyRollingFileAppender (Layout layout, String filename,
202 String datePattern) throws IOException {
203 super(layout, filename, true);
204 this.datePattern = datePattern;
205 activateOptions();
206 }
207
208
209
210
211
212
213 public void setDatePattern(String pattern) {
214 datePattern = pattern;
215 }
216
217
218 public String getDatePattern() {
219 return datePattern;
220 }
221
222 public void activateOptions() {
223 super.activateOptions();
224 if(datePattern != null && fileName != null) {
225 now.setTime(System.currentTimeMillis());
226 sdf = new SimpleDateFormat(datePattern);
227 int type = computeCheckPeriod();
228 printPeriodicity(type);
229 rc.setType(type);
230 File file = new File(fileName);
231 scheduledFilename = fileName+sdf.format(new Date(file.lastModified()));
232
233 } else {
234 LogLog.error("Either File or DatePattern options are not set for appender ["
235 +name+"].");
236 }
237 }
238
239 void printPeriodicity(int type) {
240 switch(type) {
241 case TOP_OF_MINUTE:
242 LogLog.debug("Appender ["+name+"] to be rolled every minute.");
243 break;
244 case TOP_OF_HOUR:
245 LogLog.debug("Appender ["+name
246 +"] to be rolled on top of every hour.");
247 break;
248 case HALF_DAY:
249 LogLog.debug("Appender ["+name
250 +"] to be rolled at midday and midnight.");
251 break;
252 case TOP_OF_DAY:
253 LogLog.debug("Appender ["+name
254 +"] to be rolled at midnight.");
255 break;
256 case TOP_OF_WEEK:
257 LogLog.debug("Appender ["+name
258 +"] to be rolled at start of week.");
259 break;
260 case TOP_OF_MONTH:
261 LogLog.debug("Appender ["+name
262 +"] to be rolled at start of every month.");
263 break;
264 default:
265 LogLog.warn("Unknown periodicity for appender ["+name+"].");
266 }
267 }
268
269
270
271
272
273
274
275
276
277
278
279 int computeCheckPeriod() {
280 RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault());
281
282 Date epoch = new Date(0);
283 if(datePattern != null) {
284 for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
285 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
286 simpleDateFormat.setTimeZone(gmtTimeZone);
287 String r0 = simpleDateFormat.format(epoch);
288 rollingCalendar.setType(i);
289 Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
290 String r1 = simpleDateFormat.format(next);
291
292 if(r0 != null && r1 != null && !r0.equals(r1)) {
293 return i;
294 }
295 }
296 }
297 return TOP_OF_TROUBLE;
298 }
299
300
301
302
303 void rollOver() throws IOException {
304
305
306 if (datePattern == null) {
307 errorHandler.error("Missing DatePattern option in rollOver().");
308 return;
309 }
310
311 String datedFilename = fileName+sdf.format(now);
312
313
314
315 if (scheduledFilename.equals(datedFilename)) {
316 return;
317 }
318
319
320 this.closeFile();
321
322 File target = new File(scheduledFilename);
323 if (target.exists()) {
324 target.delete();
325 }
326
327 File file = new File(fileName);
328 boolean result = file.renameTo(target);
329 if(result) {
330 LogLog.debug(fileName +" -> "+ scheduledFilename);
331 } else {
332 LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
333 }
334
335 try {
336
337
338 this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
339 }
340 catch(IOException e) {
341 errorHandler.error("setFile("+fileName+", true) call failed.");
342 }
343 scheduledFilename = datedFilename;
344 }
345
346
347
348
349
350
351
352
353
354 protected void subAppend(LoggingEvent event) {
355 long n = System.currentTimeMillis();
356 if (n >= nextCheck) {
357 now.setTime(n);
358 nextCheck = rc.getNextCheckMillis(now);
359 try {
360 rollOver();
361 }
362 catch(IOException ioe) {
363 if (ioe instanceof InterruptedIOException) {
364 Thread.currentThread().interrupt();
365 }
366 LogLog.error("rollOver() failed.", ioe);
367 }
368 }
369 super.subAppend(event);
370 }
371 }
372
373
374
375
376
377
378 class RollingCalendar extends GregorianCalendar {
379 private static final long serialVersionUID = -3560331770601814177L;
380
381 int type = DailyRollingFileAppender.TOP_OF_TROUBLE;
382
383 RollingCalendar() {
384 super();
385 }
386
387 RollingCalendar(TimeZone tz, Locale locale) {
388 super(tz, locale);
389 }
390
391 void setType(int type) {
392 this.type = type;
393 }
394
395 public long getNextCheckMillis(Date now) {
396 return getNextCheckDate(now).getTime();
397 }
398
399 public Date getNextCheckDate(Date now) {
400 this.setTime(now);
401
402 switch(type) {
403 case DailyRollingFileAppender.TOP_OF_MINUTE:
404 this.set(Calendar.SECOND, 0);
405 this.set(Calendar.MILLISECOND, 0);
406 this.add(Calendar.MINUTE, 1);
407 break;
408 case DailyRollingFileAppender.TOP_OF_HOUR:
409 this.set(Calendar.MINUTE, 0);
410 this.set(Calendar.SECOND, 0);
411 this.set(Calendar.MILLISECOND, 0);
412 this.add(Calendar.HOUR_OF_DAY, 1);
413 break;
414 case DailyRollingFileAppender.HALF_DAY:
415 this.set(Calendar.MINUTE, 0);
416 this.set(Calendar.SECOND, 0);
417 this.set(Calendar.MILLISECOND, 0);
418 int hour = get(Calendar.HOUR_OF_DAY);
419 if(hour < 12) {
420 this.set(Calendar.HOUR_OF_DAY, 12);
421 } else {
422 this.set(Calendar.HOUR_OF_DAY, 0);
423 this.add(Calendar.DAY_OF_MONTH, 1);
424 }
425 break;
426 case DailyRollingFileAppender.TOP_OF_DAY:
427 this.set(Calendar.HOUR_OF_DAY, 0);
428 this.set(Calendar.MINUTE, 0);
429 this.set(Calendar.SECOND, 0);
430 this.set(Calendar.MILLISECOND, 0);
431 this.add(Calendar.DATE, 1);
432 break;
433 case DailyRollingFileAppender.TOP_OF_WEEK:
434 this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
435 this.set(Calendar.HOUR_OF_DAY, 0);
436 this.set(Calendar.MINUTE, 0);
437 this.set(Calendar.SECOND, 0);
438 this.set(Calendar.MILLISECOND, 0);
439 this.add(Calendar.WEEK_OF_YEAR, 1);
440 break;
441 case DailyRollingFileAppender.TOP_OF_MONTH:
442 this.set(Calendar.DATE, 1);
443 this.set(Calendar.HOUR_OF_DAY, 0);
444 this.set(Calendar.MINUTE, 0);
445 this.set(Calendar.SECOND, 0);
446 this.set(Calendar.MILLISECOND, 0);
447 this.add(Calendar.MONTH, 1);
448 break;
449 default:
450 throw new IllegalStateException("Unknown periodicity type.");
451 }
452 return getTime();
453 }
454 }