KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > vfs > AbstractRolloverLog


1 /*
2  * Copyright (c) 1998-2004 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.vfs;
31
32 import java.util.ArrayList;
33
34 import java.util.logging.Logger;
35 import java.util.logging.Level;
36
37 import java.util.zip.ZipOutputStream;
38 import java.util.zip.GZIPOutputStream;
39
40 import java.io.OutputStream;
41 import java.io.IOException;
42
43 import javax.servlet.ServletContext;
44 import javax.servlet.ServletException;
45
46 import com.caucho.util.Log;
47 import com.caucho.util.L10N;
48 import com.caucho.util.Alarm;
49 import com.caucho.util.QDate;
50
51 import com.caucho.loader.Environment;
52 import com.caucho.loader.CloseListener;
53
54 import com.caucho.config.types.Period;
55 import com.caucho.config.types.Bytes;
56 import com.caucho.config.types.InitProgram;
57 import com.caucho.config.ConfigException;
58
59
60 /**
61  * Abstract class for a log that rolls over based on size or period.
62  */

63 abstract public class AbstractRolloverLog {
64   protected static final L10N L = new L10N(AbstractRolloverLog.class);
65   protected static final Logger log = Log.open(AbstractRolloverLog.class);
66
67   // Milliseconds in a day
68
private static final long DAY = 24L * 3600L * 1000L;
69   
70   // Default maximum log size = 1G
71
private static final long DEFAULT_ROLLOVER_SIZE = 1024L * 1024L * 1024L;
72   // How often to check size
73
private static final long DEFAULT_ROLLOVER_CHECK_PERIOD = 600L * 1000L;
74
75   // prefix for the rollover
76
private String _rolloverPrefix;
77
78   // template for the archived files
79
private String _archiveFormat;
80   
81   // How often the logs are rolled over.
82
private long _rolloverPeriod = Period.INFINITE;
83
84   // Maximum size of the log.
85
private long _rolloverSize = DEFAULT_ROLLOVER_SIZE;
86
87   // How often the rolloverSize should be checked
88
private long _rolloverCheckPeriod = DEFAULT_ROLLOVER_CHECK_PERIOD;
89
90   private QDate _calendar = QDate.createLocal();
91
92   protected Path _path;
93
94   protected String _pathFormat;
95
96   private String _format;
97
98   // The time of the next period-based rollover
99
private long _nextPeriodEnd = -1;
100   private long _nextRolloverCheckTime = -1;
101
102   private WriteStream _os;
103
104   /**
105    * Returns the access-log's path.
106    */

107   public Path getPath()
108   {
109     return _path;
110   }
111
112   /**
113    * Sets the access-log's path.
114    */

115   public void setPath(Path path)
116   {
117     _path = path;
118   }
119
120   /**
121    * Returns the formatted path
122    */

123   public String getPathFormat()
124   {
125     return _pathFormat;
126   }
127
128   /**
129    * Sets the formatted path.
130    */

131   public void setPathFormat(String pathFormat)
132   {
133     _pathFormat = pathFormat;
134   }
135
136   /**
137    * Sets the archive name format
138    */

139   public void setArchiveFormat(String format)
140   {
141     _archiveFormat = format;
142   }
143
144   /**
145    * Sets the archive name format
146    */

147   public String getArchiveFormat()
148   {
149     return _archiveFormat;
150   }
151
152   /**
153    * Sets the log rollover period, rounded up to the nearest hour.
154    *
155    * @param period the new rollover period in milliseconds.
156    */

157   public void setRolloverPeriod(Period period)
158   {
159     _rolloverPeriod = period.getPeriod();
160     
161     if (_rolloverPeriod > 0) {
162       _rolloverPeriod += 3600000L - 1;
163       _rolloverPeriod -= _rolloverPeriod % 3600000L;
164     }
165     else
166       _rolloverPeriod = Period.INFINITE;
167   }
168
169   /**
170    * Sets the log rollover period, rounded up to the nearest hour.
171    *
172    * @param period the new rollover period in milliseconds.
173    */

174   public long getRolloverPeriod()
175   {
176     return _rolloverPeriod;
177   }
178
179   /**
180    * Sets the log rollover size, rounded up to the megabyte.
181    *
182    * @param size maximum size of the log file
183    */

184   public void setRolloverSize(Bytes bytes)
185   {
186     long size = bytes.getBytes();
187     
188     if (size < 0)
189       _rolloverSize = Bytes.INFINITE;
190     else
191       _rolloverSize = size;
192   }
193
194   /**
195    * Sets the log rollover size, rounded up to the megabyte.
196    *
197    * @param size maximum size of the log file
198    */

199   public long getRolloverSize()
200   {
201     return _rolloverSize;
202   }
203
204   /**
205    * Sets how often the log rollover will be checked.
206    *
207    * @param period how often the log rollover will be checked.
208    */

209   public void setRolloverCheckPeriod(long period)
210   {
211     if (period > 1000)
212       _rolloverCheckPeriod = period;
213     else if (period > 0)
214       _rolloverCheckPeriod = 1000;
215   }
216
217   /**
218    * Sets how often the log rollover will be checked.
219    *
220    * @param period how often the log rollover will be checked.
221    */

222   public long getRolloverCheckPeriod()
223   {
224     return _rolloverCheckPeriod;
225   }
226   
227   /**
228    * Initialize the log.
229    */

230   public void init()
231     throws ServletException, IOException
232   {
233     long now = Alarm.getCurrentTime();
234     
235     _nextRolloverCheckTime = now + _rolloverCheckPeriod;
236
237     Path path = getPath();
238
239     if (path != null) {
240       path.getParent().mkdirs();
241     
242       _rolloverPrefix = path.getTail();
243
244       long lastModified = path.getLastModified();
245       if (lastModified <= 0)
246     lastModified = now;
247     
248       _calendar.setGMTTime(lastModified);
249       long zone = _calendar.getZoneOffset();
250
251       _nextPeriodEnd = Period.periodEnd(lastModified, getRolloverPeriod());
252     }
253     else
254       _nextPeriodEnd = Period.periodEnd(now, getRolloverPeriod());
255
256     if (_nextPeriodEnd < _nextRolloverCheckTime && _nextPeriodEnd > 0)
257       _nextRolloverCheckTime = _nextPeriodEnd;
258
259     rolloverLog(now);
260   }
261
262   public boolean isRollover()
263   {
264     long now = Alarm.getCurrentTime();
265     
266     return _nextPeriodEnd <= now || _nextRolloverCheckTime <= now;
267   }
268
269   /**
270    * Writes to the underlying log.
271    */

272   protected void write(byte []buffer, int offset, int length)
273     throws IOException
274   {
275     if (_os == null)
276       openLog();
277
278     if (_os != null)
279       _os.write(buffer, offset, length);
280   }
281
282   /**
283    * Writes to the underlying log.
284    */

285   protected void flush()
286     throws IOException
287   {
288     if (_os != null)
289       _os.flush();
290   }
291
292   /**
293    * Check to see if we need to rollover the log.
294    *
295    * @param now current time in milliseconds.
296    */

297   protected void rolloverLog(long now)
298   {
299     _nextRolloverCheckTime = now + _rolloverCheckPeriod;
300
301     Path path = getPath();
302       
303     if (_nextPeriodEnd < now) {
304       closeLogStream();
305       
306       if (getPathFormat() == null) {
307     Path savedPath = getArchivePath(_nextPeriodEnd - 1);
308     movePathToArchive(savedPath);
309       }
310     
311       _nextPeriodEnd = Period.periodEnd(now, getRolloverPeriod());
312       
313       if (log.isLoggable(Level.FINE))
314     log.fine(getPath() + ": next rollover at " +
315          QDate.formatLocal(_nextPeriodEnd));
316     }
317     else if (path != null && getRolloverSize() <= path.getLength()) {
318       closeLogStream();
319       
320       if (getPathFormat() == null) {
321     Path savedPath = getArchivePath(_nextRolloverCheckTime - 1);
322     movePathToArchive(savedPath);
323       }
324     }
325
326     long nextPeriodEnd = _nextPeriodEnd;
327     if (_nextPeriodEnd < _nextRolloverCheckTime && _nextPeriodEnd > 0)
328       _nextRolloverCheckTime = _nextPeriodEnd;
329
330     if (_os == null)
331       openLog();
332   }
333
334   /**
335    * Tries to open the log.
336    */

337   private void openLog()
338   {
339     closeLogStream();
340     
341     try {
342       WriteStream os = _os;
343       _os = null;
344
345       if (os != null)
346     os.close();
347     } catch (Throwable e) {
348       log.log(Level.FINER, e.toString(), e);
349     }
350       
351     Path path = getPath();
352
353     if (path == null) {
354       path = getPath(Alarm.getCurrentTime());
355     }
356     
357     try {
358       if (! path.getParent().isDirectory())
359     path.getParent().mkdirs();
360     } catch (Throwable e) {
361       log.log(Level.FINER, e.toString(), e);
362     }
363     
364     for (int i = 0; i < 3 && _os == null; i++) {
365       try {
366     _os = path.openAppend();
367       } catch (IOException e) {
368     log.log(Level.INFO, e.toString(), e);
369       }
370     }
371
372     if (_os == null)
373       log.warning(L.l("Can't open access log file '{0}'.",
374               getPath()));
375   }
376
377   /**
378    * Tries to open the log.
379    */

380   private void closeLogStream()
381   {
382     try {
383       WriteStream os = _os;
384       _os = null;
385
386       if (os != null)
387     os.close();
388     } catch (Throwable e) {
389       log.log(Level.FINER, e.toString(), e);
390     }
391   }
392
393   private void movePathToArchive(Path savedPath)
394   {
395     Path path = getPath();
396     
397     String savedName = savedPath.getTail();
398     
399     log.info(L.l("Archiving access log to {0}.", savedName));
400          
401     try {
402       WriteStream os = _os;
403       _os = null;
404       if (os != null)
405     os.close();
406     } catch (IOException e) {
407       log.log(Level.FINE, e.toString(), e);
408     }
409
410     try {
411       savedPath.getParent().mkdirs();
412     } catch (Throwable e) {
413       log.log(Level.WARNING, e.toString(), e);
414     }
415         
416     try {
417       WriteStream os = savedPath.openWrite();
418       OutputStream out;
419
420       if (savedName.endsWith(".gz"))
421     out = new GZIPOutputStream(os);
422       else if (savedName.endsWith(".zip"))
423     out = new ZipOutputStream(os);
424       else
425     out = os;
426
427       try {
428     path.writeToStream(out);
429       } finally {
430     try {
431       out.close();
432     } catch (Throwable e) {
433       log.log(Level.WARNING, e.toString(), e);
434     }
435
436     try {
437       if (out != os)
438         os.close();
439     } catch (Throwable e) {
440       log.log(Level.WARNING, e.toString(), e);
441     }
442       
443     path.remove();
444       }
445     } catch (Throwable e) {
446       log.log(Level.WARNING, e.toString(), e);
447     }
448   }
449
450   /**
451    * Returns the path of the format file
452    *
453    * @param time the archive date
454    */

455   protected Path getPath(long time)
456   {
457     String formatString = getPathFormat();
458
459     if (formatString == null)
460       throw new IllegalStateException(L.l("getPath requires a format path"));
461     
462     String pathString = getFormatName(formatString, time);
463
464     return Vfs.lookup().lookup(pathString);
465   }
466
467   /**
468    * Returns the name of the archived file
469    *
470    * @param time the archive date
471    */

472   protected Path getArchivePath(long time)
473   {
474     Path path = getPath();
475
476     String archiveFormat = getArchiveFormat();
477     
478     String name = getFormatName(archiveFormat, time);
479
480     Path newPath = path.getParent().lookup(name);
481
482     if (newPath.exists()) {
483       if (archiveFormat == null)
484     archiveFormat = _rolloverPrefix + ".%Y%m%d.%H%M";
485       else if (! archiveFormat.contains("%H"))
486     archiveFormat = archiveFormat + ".%H%M";
487       else if (! archiveFormat.contains("%M"))
488     archiveFormat = archiveFormat + ".%M";
489       
490       name = getFormatName(archiveFormat, time);
491
492       newPath = path.getParent().lookup(name);
493     }
494
495     return newPath;
496   }
497
498   /**
499    * Returns the name of the archived file
500    *
501    * @param time the archive date
502    */

503   protected String getFormatName(String format, long time)
504   {
505     if (time <= 0)
506       time = Alarm.getCurrentTime();
507     
508     if (format != null)
509       return _calendar.formatLocal(time, format);
510     else if (getRolloverPeriod() % (24 * 3600 * 1000L) == 0)
511       return _rolloverPrefix + "." + _calendar.formatLocal(time, "%Y%m%d");
512     else
513       return _rolloverPrefix + "." + _calendar.formatLocal(time, "%Y%m%d.%H");
514   }
515
516   /**
517    * Closes the log, flushing the results.
518    */

519   public void close()
520     throws IOException
521   {
522     if (_os != null) {
523       WriteStream os = _os;
524       _os = null;
525       
526       os.close();
527     }
528   }
529 }
530
Popular Tags