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