解决log4j不能rename的问题

项目中使用到了log4j包,之前是一天生成一个log文件。之后需要改为一小时生成一个,于是乎,测试的时候发现出现了不能rename的问题,查找原因,发现原来是文件正在被java虚拟机使用。之后就到网上搜了一下,发现大家都出现过这种问题,于是看到了大家的各种解决办法,但都是一种思路,那就是将将文件copy之后再进行rename。具体的做法就是新建一个DailyRollingFileAppender类,当然包名仍然是org.apache.log4j,其具体内容如下:

View Code
  1 /*
  2  * Licensed to the Apache Software Foundation (ASF) under one or more
  3  * contributor license agreements.  See the NOTICE file distributed with
  4  * this work for additional information regarding copyright ownership.
  5  * The ASF licenses this file to You under the Apache License, Version 2.0
  6  * (the "License"); you may not use this file except in compliance with
  7  * the License.  You may obtain a copy of the License at
  8  * 
  9  *      http://www.apache.org/licenses/LICENSE-2.0
 10  * 
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  */
 17 
 18 package org.apache.log4j;
 19 
 20 import java.io.FileInputStream;
 21 import java.io.FileNotFoundException;
 22 import java.io.FileOutputStream;
 23 import java.io.IOException;
 24 import java.io.File;
 25 import java.io.InputStream;
 26 import java.io.OutputStream;
 27 import java.text.SimpleDateFormat;
 28 import java.util.Date;
 29 import java.util.GregorianCalendar;
 30 import java.util.Calendar;
 31 import java.util.TimeZone;
 32 import java.util.Locale;
 33 
 34 import org.apache.log4j.helpers.LogLog;
 35 import org.apache.log4j.spi.LoggingEvent;
 36 
 37 /**
 38  * DailyRollingFileAppender extends {@link FileAppender} so that the underlying
 39  * file is rolled over at a user chosen frequency.
 40  * 
 41  * <p>
 42  * The rolling schedule is specified by the <b>DatePattern</b> option. This
 43  * pattern should follow the {@link SimpleDateFormat} conventions. In
 44  * particular, you <em>must</em> escape literal text within a pair of single
 45  * quotes. A formatted version of the date pattern is used as the suffix for the
 46  * rolled file name.
 47  * 
 48  * <p>
 49  * For example, if the <b>File</b> option is set to <code>/foo/bar.log</code>
 50  * and the <b>DatePattern</b> set to <code>'.'yyyy-MM-dd</code>, on
 51  * 2001-02-16 at midnight, the logging file <code>/foo/bar.log</code> will be
 52  * copied to <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17
 53  * will continue in <code>/foo/bar.log</code> until it rolls over the next
 54  * day.
 55  * 
 56  * <p>
 57  * Is is possible to specify monthly, weekly, half-daily, daily, hourly, or
 58  * minutely rollover schedules.
 59  * 
 60  * <p>
 61  * <table border="1" cellpadding="2">
 62  * <tr>
 63  * <th>DatePattern</th>
 64  * <th>Rollover schedule</th>
 65  * <th>Example</th>
 66  * 
 67  * <tr>
 68  * <td><code>'.'yyyy-MM</code>
 69  * <td>Rollover at the beginning of each month</td>
 70  * 
 71  * <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be copied
 72  * to <code>/foo/bar.log.2002-05</code>. Logging for the month of June will
 73  * be output to <code>/foo/bar.log</code> until it is also rolled over the
 74  * next month.
 75  * 
 76  * <tr>
 77  * <td><code>'.'yyyy-ww</code>
 78  * 
 79  * <td>Rollover at the first day of each week. The first day of the week
 80  * depends on the locale.</td>
 81  * 
 82  * <td>Assuming the first day of the week is Sunday, on Saturday midnight, June
 83  * 9th 2002, the file <i>/foo/bar.log</i> will be copied to
 84  * <i>/foo/bar.log.2002-23</i>. Logging for the 24th week of 2002 will be
 85  * output to <code>/foo/bar.log</code> until it is rolled over the next week.
 86  * 
 87  * <tr>
 88  * <td><code>'.'yyyy-MM-dd</code>
 89  * 
 90  * <td>Rollover at midnight each day.</td>
 91  * 
 92  * <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will be
 93  * copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the 9th day of
 94  * March will be output to <code>/foo/bar.log</code> until it is rolled over
 95  * the next day.
 96  * 
 97  * <tr>
 98  * <td><code>'.'yyyy-MM-dd-a</code>
 99  * 
100  * <td>Rollover at midnight and midday of each day.</td>
101  * 
102  * <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be copied
103  * to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the afternoon of
104  * the 9th will be output to <code>/foo/bar.log</code> until it is rolled over
105  * at midnight.
106  * 
107  * <tr>
108  * <td><code>'.'yyyy-MM-dd-HH</code>
109  * 
110  * <td>Rollover at the top of every hour.</td>
111  * 
112  * <td>At approximately 11:00.000 o'clock on March 9th, 2002,
113  * <code>/foo/bar.log</code> will be copied to
114  * <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour of the
115  * 9th of March will be output to <code>/foo/bar.log</code> until it is rolled
116  * over at the beginning of the next hour.
117  * 
118  * 
119  * <tr>
120  * <td><code>'.'yyyy-MM-dd-HH-mm</code>
121  * 
122  * <td>Rollover at the beginning of every minute.</td>
123  * 
124  * <td>At approximately 11:23,000, on March 9th, 2001,
125  * <code>/foo/bar.log</code> will be copied to
126  * <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute of
127  * 11:23 (9th of March) will be output to <code>/foo/bar.log</code> until it
128  * is rolled over the next minute.
129  * 
130  * </table>
131  * 
132  * <p>
133  * Do not use the colon ":" character in anywhere in the <b>DatePattern</b>
134  * option. The text before the colon is interpeted as the protocol specificaion
135  * of a URL which is probably not what you want.
136  * 
137  * 
138  * @author Eirik Lygre
139  * @author Ceki G&uuml;lc&uuml;
140  */
141 public class DailyRollingFileAppender extends FileAppender {
142 
143     // The code assumes that the following constants are in a increasing
144     // sequence.
145     static final int TOP_OF_TROUBLE = -1;
146 
147     static final int TOP_OF_MINUTE = 0;
148 
149     static final int TOP_OF_HOUR = 1;
150 
151     static final int HALF_DAY = 2;
152 
153     static final int TOP_OF_DAY = 3;
154 
155     static final int TOP_OF_WEEK = 4;
156 
157     static final int TOP_OF_MONTH = 5;
158 
159     /**
160      * The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd"
161      * meaning daily rollover.
162      */
163     private String datePattern = "'.'yyyy-MM-dd";
164 
165     /**
166      * The log file will be renamed to the value of the scheduledFilename
167      * variable when the next interval is entered. For example, if the rollover
168      * period is one hour, the log file will be renamed to the value of
169      * "scheduledFilename" at the beginning of the next hour.
170      * 
171      * The precise time when a rollover occurs depends on logging activity.
172      */
173     private String scheduledFilename;
174 
175     /**
176      * The next time we estimate a rollover should occur.
177      */
178     private long nextCheck = System.currentTimeMillis() - 1;
179 
180     Date now = new Date();
181 
182     SimpleDateFormat sdf;
183 
184     RollingCalendar rc = new RollingCalendar();
185 
186     int checkPeriod = TOP_OF_TROUBLE;
187 
188     // The gmtTimeZone is used only in computeCheckPeriod() method.
189     static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
190 
191     /**
192      * The default constructor does nothing.
193      */
194     public DailyRollingFileAppender() {
195     }
196 
197     /**
198      * Instantiate a <code>DailyRollingFileAppender</code> and open the file
199      * designated by <code>filename</code>. The opened filename will become
200      * the ouput destination for this appender.
201      * 
202      */
203     public DailyRollingFileAppender(Layout layout, String filename,
204             String datePattern) throws IOException {
205         super(layout, filename, true);
206         this.datePattern = datePattern;
207         activateOptions();
208     }
209 
210     /**
211      * The <b>DatePattern</b> takes a string in the same format as expected by
212      * {@link SimpleDateFormat}. This options determines the rollover schedule.
213      */
214     public void setDatePattern(String pattern) {
215         datePattern = pattern;
216     }
217 
218     /** Returns the value of the <b>DatePattern</b> option. */
219     public String getDatePattern() {
220         return datePattern;
221     }
222 
223     public void activateOptions() {
224         super.activateOptions();
225         if (datePattern != null && fileName != null) {
226             now.setTime(System.currentTimeMillis());
227             sdf = new SimpleDateFormat(datePattern);
228             int type = computeCheckPeriod();
229             printPeriodicity(type);
230             rc.setType(type);
231             File file = new File(fileName);
232             scheduledFilename = fileName
233                     + sdf.format(new Date(file.lastModified()));
234 
235         } else {
236             LogLog
237                     .error("Either File or DatePattern options are not set for appender ["
238                             + name + "].");
239         }
240     }
241 
242     void printPeriodicity(int type) {
243         switch (type) {
244         case TOP_OF_MINUTE:
245             LogLog.debug("Appender [" + name + "] to be rolled every minute.");
246             break;
247         case TOP_OF_HOUR:
248             LogLog.debug("Appender [" + name
249                     + "] to be rolled on top of every hour.");
250             break;
251         case HALF_DAY:
252             LogLog.debug("Appender [" + name
253                     + "] to be rolled at midday and midnight.");
254             break;
255         case TOP_OF_DAY:
256             LogLog.debug("Appender [" + name + "] to be rolled at midnight.");
257             break;
258         case TOP_OF_WEEK:
259             LogLog.debug("Appender [" + name
260                     + "] to be rolled at start of week.");
261             break;
262         case TOP_OF_MONTH:
263             LogLog.debug("Appender [" + name
264                     + "] to be rolled at start of every month.");
265             break;
266         default:
267             LogLog.warn("Unknown periodicity for appender [" + name + "].");
268         }
269     }
270 
271     // This method computes the roll over period by looping over the
272     // periods, starting with the shortest, and stopping when the r0 is
273     // different from from r1, where r0 is the epoch formatted according
274     // the datePattern (supplied by the user) and r1 is the
275     // epoch+nextMillis(i) formatted according to datePattern. All date
276     // formatting is done in GMT and not local format because the test
277     // logic is based on comparisons relative to 1970-01-01 00:00:00
278     // GMT (the epoch).
279 
280     int computeCheckPeriod() {
281         RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone,
282                 Locale.ENGLISH);
283         // set sate to 1970-01-01 00:00:00 GMT
284         Date epoch = new Date(0);
285         if (datePattern != null) {
286             for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
287                 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
288                         datePattern);
289                 simpleDateFormat.setTimeZone(gmtTimeZone); // do all date
290                 // formatting in GMT
291                 String r0 = simpleDateFormat.format(epoch);
292                 rollingCalendar.setType(i);
293                 Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
294                 String r1 = simpleDateFormat.format(next);
295                 // System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
296                 if (r0 != null && r1 != null && !r0.equals(r1)) {
297                     return i;
298                 }
299             }
300         }
301         return TOP_OF_TROUBLE; // Deliberately head for trouble...
302     }
303 
304     /**
305      * Rollover the current file to a new file.
306      */
307     void rollOver() throws IOException {
308 
309         /* Compute filename, but only if datePattern is specified */
310         if (datePattern == null) {
311             errorHandler.error("Missing DatePattern option in rollOver().");
312             return;
313         }
314 
315         String datedFilename = fileName + sdf.format(now);
316         // It is too early to roll over because we are still within the
317         // bounds of the current interval. Rollover will occur once the
318         // next interval is reached.
319         if (scheduledFilename.equals(datedFilename)) {
320             return;
321         }
322 
323         // close current file, and rename it to datedFilename
324         this.closeFile();
325 
326         File target = new File(scheduledFilename);
327         if (target.exists()) {
328             target.delete();
329         }
330 
331         File file = new File(fileName);
332         boolean result = copy(file, target);
333         if (result) {
334             LogLog.debug(fileName + " -> " + scheduledFilename);
335         } else {
336             LogLog.error("Failed to rename [" + fileName + "] to ["
337                     + scheduledFilename + "].");
338         }
339 
340         try {
341             // This will also close the file. This is OK since multiple
342             // close operations are safe.
343             this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
344         } catch (IOException e) {
345             errorHandler.error("setFile(" + fileName + ", false) call failed.");
346         }
347         scheduledFilename = datedFilename;
348     }
349 
350     /**
351      * Copies src file to dst file. If the dst file does not exist, it is
352      * created.8KB cache
353      * 
354      * @param src
355      * @param dst
356      * @throws IOException
357      */
358     boolean copy(File src, File dst) throws IOException {
359         try {
360             InputStream in = new FileInputStream(src);
361 
362             OutputStream out = new FileOutputStream(dst);
363 
364             // Transfer bytes from in to out
365             byte[] buf = new byte[8192];
366             int len;
367             while ((len = in.read(buf)) > 0) {
368                 out.write(buf, 0, len);
369             }
370             in.close();
371             out.close();
372             return true;
373         } catch (FileNotFoundException e) {
374             LogLog.error("源文件不存在,或者目标文件无法被识别." + e);
375             return false;
376         } catch (IOException e) {
377             LogLog.error("文件读写错误." + e);
378             return false;
379         }
380     }
381 
382     /**
383      * This method differentiates DailyRollingFileAppender from its super class.
384      * 
385      * <p>
386      * Before actually logging, this method will check whether it is time to do
387      * a rollover. If it is, it will schedule the next rollover time and then
388      * rollover.
389      */
390     protected void subAppend(LoggingEvent event) {
391         long n = System.currentTimeMillis();
392         if (n >= nextCheck) {
393             now.setTime(n);
394             nextCheck = rc.getNextCheckMillis(now);
395             try {
396                 rollOver();
397             } catch (IOException ioe) {
398                 LogLog.error("rollOver() failed.", ioe);
399             }
400         }
401         super.subAppend(event);
402     }
403 }
404 
405 /**
406  * RollingCalendar is a helper class to DailyRollingFileAppender. Given a
407  * periodicity type and the current time, it computes the start of the next
408  * interval.
409  */
410 class RollingCalendar extends GregorianCalendar {
411     private static final long serialVersionUID = -3560331770601814177L;
412 
413     int type = DailyRollingFileAppender.TOP_OF_TROUBLE;
414 
415     RollingCalendar() {
416         super();
417     }
418 
419     RollingCalendar(TimeZone tz, Locale locale) {
420         super(tz, locale);
421     }
422 
423     void setType(int type) {
424         this.type = type;
425     }
426 
427     public long getNextCheckMillis(Date now) {
428         return getNextCheckDate(now).getTime();
429     }
430 
431     public Date getNextCheckDate(Date now) {
432         this.setTime(now);
433 
434         switch (type) {
435         case DailyRollingFileAppender.TOP_OF_MINUTE:
436             this.set(Calendar.SECOND, 0);
437             this.set(Calendar.MILLISECOND, 0);
438             this.add(Calendar.MINUTE, 1);
439             break;
440         case DailyRollingFileAppender.TOP_OF_HOUR:
441             this.set(Calendar.MINUTE, 0);
442             this.set(Calendar.SECOND, 0);
443             this.set(Calendar.MILLISECOND, 0);
444             this.add(Calendar.HOUR_OF_DAY, 1);
445             break;
446         case DailyRollingFileAppender.HALF_DAY:
447             this.set(Calendar.MINUTE, 0);
448             this.set(Calendar.SECOND, 0);
449             this.set(Calendar.MILLISECOND, 0);
450             int hour = get(Calendar.HOUR_OF_DAY);
451             if (hour < 12) {
452                 this.set(Calendar.HOUR_OF_DAY, 12);
453             } else {
454                 this.set(Calendar.HOUR_OF_DAY, 0);
455                 this.add(Calendar.DAY_OF_MONTH, 1);
456             }
457             break;
458         case DailyRollingFileAppender.TOP_OF_DAY:
459             this.set(Calendar.HOUR_OF_DAY, 0);
460             this.set(Calendar.MINUTE, 0);
461             this.set(Calendar.SECOND, 0);
462             this.set(Calendar.MILLISECOND, 0);
463             this.add(Calendar.DATE, 1);
464             break;
465         case DailyRollingFileAppender.TOP_OF_WEEK:
466             this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
467             this.set(Calendar.HOUR_OF_DAY, 0);
468             this.set(Calendar.MINUTE, 0);
469             this.set(Calendar.SECOND, 0);
470             this.set(Calendar.MILLISECOND, 0);
471             this.add(Calendar.WEEK_OF_YEAR, 1);
472             break;
473         case DailyRollingFileAppender.TOP_OF_MONTH:
474             this.set(Calendar.DATE, 1);
475             this.set(Calendar.HOUR_OF_DAY, 0);
476             this.set(Calendar.MINUTE, 0);
477             this.set(Calendar.SECOND, 0);
478             this.set(Calendar.MILLISECOND, 0);
479             this.add(Calendar.MONTH, 1);
480             break;
481         default:
482             throw new IllegalStateException("Unknown periodicity type.");
483         }
484         return getTime();
485     }
486 }

之后就可以啦!当然还有一种方案就是将原文件中的该类修改之后重新打包,把原来的包替换为修改之后的就OK了。

posted @ 2013-03-26 13:06  Jerry Ru  阅读(638)  评论(0编辑  收藏  举报