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