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