1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.util;
20
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.FilterOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.text.SimpleDateFormat;
27 import java.util.Calendar;
28 import java.util.Date;
29 import java.util.GregorianCalendar;
30 import java.util.Locale;
31 import java.util.TimeZone;
32 import java.util.Timer;
33 import java.util.TimerTask;
34
35
36
37
38
39
40
41
42
43
44 public class RolloverFileOutputStream extends FilterOutputStream
45 {
46 private static Timer __rollover;
47
48 final static String YYYY_MM_DD="yyyy_mm_dd";
49 final static String ROLLOVER_FILE_DATE_FORMAT = "yyyy_MM_dd";
50 final static String ROLLOVER_FILE_BACKUP_FORMAT = "HHmmssSSS";
51 final static int ROLLOVER_FILE_RETAIN_DAYS = 31;
52
53 private RollTask _rollTask;
54 private SimpleDateFormat _fileBackupFormat;
55 private SimpleDateFormat _fileDateFormat;
56
57 private String _filename;
58 private File _file;
59 private boolean _append;
60 private int _retainDays;
61
62
63
64
65
66
67
68 public RolloverFileOutputStream(String filename)
69 throws IOException
70 {
71 this(filename,true,ROLLOVER_FILE_RETAIN_DAYS);
72 }
73
74
75
76
77
78
79
80
81 public RolloverFileOutputStream(String filename, boolean append)
82 throws IOException
83 {
84 this(filename,append,ROLLOVER_FILE_RETAIN_DAYS);
85 }
86
87
88
89
90
91
92
93
94
95 public RolloverFileOutputStream(String filename,
96 boolean append,
97 int retainDays)
98 throws IOException
99 {
100 this(filename,append,retainDays,TimeZone.getDefault());
101 }
102
103
104
105
106
107
108
109
110
111
112 public RolloverFileOutputStream(String filename,
113 boolean append,
114 int retainDays,
115 TimeZone zone)
116 throws IOException
117 {
118
119 this(filename,append,retainDays,zone,null,null);
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133 public RolloverFileOutputStream(String filename,
134 boolean append,
135 int retainDays,
136 TimeZone zone,
137 String dateFormat,
138 String backupFormat)
139 throws IOException
140 {
141 super(null);
142
143 if (dateFormat==null)
144 dateFormat=ROLLOVER_FILE_DATE_FORMAT;
145 _fileDateFormat = new SimpleDateFormat(dateFormat);
146
147 if (backupFormat==null)
148 backupFormat=ROLLOVER_FILE_BACKUP_FORMAT;
149 _fileBackupFormat = new SimpleDateFormat(backupFormat);
150
151 _fileBackupFormat.setTimeZone(zone);
152 _fileDateFormat.setTimeZone(zone);
153
154 if (filename!=null)
155 {
156 filename=filename.trim();
157 if (filename.length()==0)
158 filename=null;
159 }
160 if (filename==null)
161 throw new IllegalArgumentException("Invalid filename");
162
163 _filename=filename;
164 _append=append;
165 _retainDays=retainDays;
166 setFile();
167
168 synchronized(RolloverFileOutputStream.class)
169 {
170 if (__rollover==null)
171 __rollover=new Timer(RolloverFileOutputStream.class.getName(),true);
172
173 _rollTask=new RollTask();
174
175 Calendar now = Calendar.getInstance();
176 now.setTimeZone(zone);
177
178 GregorianCalendar midnight =
179 new GregorianCalendar(now.get(Calendar.YEAR),
180 now.get(Calendar.MONTH),
181 now.get(Calendar.DAY_OF_MONTH),
182 23,0);
183 midnight.setTimeZone(zone);
184 midnight.add(Calendar.HOUR,1);
185 __rollover.scheduleAtFixedRate(_rollTask,midnight.getTime(),1000L*60*60*24);
186 }
187 }
188
189
190 public String getFilename()
191 {
192 return _filename;
193 }
194
195
196 public String getDatedFilename()
197 {
198 if (_file==null)
199 return null;
200 return _file.toString();
201 }
202
203
204 public int getRetainDays()
205 {
206 return _retainDays;
207 }
208
209
210 private synchronized void setFile()
211 throws IOException
212 {
213
214 File file = new File(_filename);
215 _filename=file.getCanonicalPath();
216 file=new File(_filename);
217 File dir= new File(file.getParent());
218 if (!dir.isDirectory() || !dir.canWrite())
219 throw new IOException("Cannot write log directory "+dir);
220
221 Date now=new Date();
222
223
224 String filename=file.getName();
225 int i=filename.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
226 if (i>=0)
227 {
228 file=new File(dir,
229 filename.substring(0,i)+
230 _fileDateFormat.format(now)+
231 filename.substring(i+YYYY_MM_DD.length()));
232 }
233
234 if (file.exists()&&!file.canWrite())
235 throw new IOException("Cannot write log file "+file);
236
237
238 if (out==null || !file.equals(_file))
239 {
240
241 _file=file;
242 if (!_append && file.exists())
243 file.renameTo(new File(file.toString()+"."+_fileBackupFormat.format(now)));
244 OutputStream oldOut=out;
245 out=new FileOutputStream(file.toString(),_append);
246 if (oldOut!=null)
247 oldOut.close();
248
249 }
250 }
251
252
253 private void removeOldFiles()
254 {
255 if (_retainDays>0)
256 {
257 long now = System.currentTimeMillis();
258
259 File file= new File(_filename);
260 File dir = new File(file.getParent());
261 String fn=file.getName();
262 int s=fn.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
263 if (s<0)
264 return;
265 String prefix=fn.substring(0,s);
266 String suffix=fn.substring(s+YYYY_MM_DD.length());
267
268 String[] logList=dir.list();
269 for (int i=0;i<logList.length;i++)
270 {
271 fn = logList[i];
272 if(fn.startsWith(prefix)&&fn.indexOf(suffix,prefix.length())>=0)
273 {
274 File f = new File(dir,fn);
275 long date = f.lastModified();
276 if ( ((now-date)/(1000*60*60*24))>_retainDays)
277 f.delete();
278 }
279 }
280 }
281 }
282
283
284 @Override
285 public void write (byte[] buf)
286 throws IOException
287 {
288 out.write (buf);
289 }
290
291
292 @Override
293 public void write (byte[] buf, int off, int len)
294 throws IOException
295 {
296 out.write (buf, off, len);
297 }
298
299
300
301
302 @Override
303 public void close()
304 throws IOException
305 {
306 synchronized(RolloverFileOutputStream.class)
307 {
308 try{super.close();}
309 finally
310 {
311 out=null;
312 _file=null;
313 }
314
315 _rollTask.cancel();
316 }
317 }
318
319
320
321
322 private class RollTask extends TimerTask
323 {
324 @Override
325 public void run()
326 {
327 try
328 {
329 RolloverFileOutputStream.this.setFile();
330 RolloverFileOutputStream.this.removeOldFiles();
331
332 }
333 catch(IOException e)
334 {
335
336 e.printStackTrace();
337 }
338 }
339 }
340 }