題記------學習別人的精髓,並加以總結,消化吸收,這就是提高!!!
DailyRollingFileAppender生成的文件是不帶時間戳的,必須在某個時間點后,才對原來文件加上時間戳進行重命名,這樣就有很大的問題,當天的日志,沒有時間戳,而且如果在log4j.properties配置文件路徑采用log4j.appender.dailyFile.File=E:/logs/log_或者log4j.appender.dailyFile.File=${webapp.root}/WEB-INF/logs/log_這種命名方式的,那么恭喜你,你生成的日志文件連后綴名都沒有,想打開還得自己加后綴名,比如.log,.xml什么的總之很不方便。在此處說句題外話,就是解釋下什么是${webapp.root},這個說簡單點就是你項目運行環境的絕對路徑,一般在web.xml中如果我們不配置webAppRootKey默認的缺省值就是${webapp.root},不過本人還是建議大家配置一下webAppRootKey,這樣在同一服務器下運行多個項目就沒沖突了。
log4j真的是一款很強大的日志開源框架,免費且好用,當然也會略有瑕疵,如果項目允許,我建議小伙伴們還是采用logback框架吧,這個框架不僅效率高,而且還規避了本文談及的一系列問題,更關鍵的是如果你會用log4j那么學會使用logback簡直是小kiss,廢話不多說,這次主要是重新了log4j的兩個類,FileAppender及其子類DailyRollingFileAppender,由於篇幅太多我就不一一贅述代碼改動了,同志們如果想知道改了哪些,可以采用代碼比對工具Beyond Compare 4,下載地址http://download.csdn.net/download/gongxun344/9982938,附apache-log4j-1.2.17源碼地址http://download.csdn.net/download/gongxun344/9982916。話不多說,上本人修改后的源碼:
1、修改FileAppender類
1 // Decompiled by DJ v3.12.12.100 Copyright 2015 Atanas Neshkov Date: 2017-09-16 21:34:52 2 // Home Page: http://www.neshkov.com/dj.html - Check often for new version! 3 // Decompiler options: packimports(3) 4 // Source File Name: FileAppender.java 5 6 package com.gongli.log; 7 8 import java.io.*; 9 import java.util.Date; 10 import java.text.SimpleDateFormat; 11 12 import org.apache.log4j.Layout; 13 import com.gongli.log.WriterAppender; 14 import org.apache.log4j.helpers.LogLog; 15 import org.apache.log4j.helpers.QuietWriter; 16 17 // Referenced classes of package org.apache.log4j: 18 // WriterAppender, Layout 19 20 public class FileAppender extends WriterAppender 21 { 22 private String datePattern = "yyyy-MM-dd"; 23 24 public FileAppender() 25 { 26 fileAppend = true; 27 fileName = null; 28 bufferedIO = false; 29 bufferSize = 8192; 30 } 31 32 public FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO, int bufferSize) 33 throws IOException 34 { 35 fileAppend = true; 36 fileName = null; 37 this.bufferedIO = false; 38 this.bufferSize = 8192; 39 this.layout = layout; 40 setFile(filename, append, bufferedIO, bufferSize); 41 } 42 43 public FileAppender(Layout layout, String filename, boolean append) 44 throws IOException 45 { 46 fileAppend = true; 47 fileName = null; 48 bufferedIO = false; 49 bufferSize = 8192; 50 this.layout = layout; 51 setFile(filename, append, false, bufferSize); 52 } 53 54 public FileAppender(Layout layout, String filename) 55 throws IOException 56 { 57 this(layout, filename, true); 58 } 59 60 public void setFile(String file) 61 { 62 String val = file.trim(); 63 fileName = val; 64 } 65 66 public boolean getAppend() 67 { 68 return fileAppend; 69 } 70 71 public String getFile() 72 { 73 return fileName; 74 } 75 76 public void activateOptions() 77 { 78 SimpleDateFormat sdf = new SimpleDateFormat(datePattern); 79 Date now = new Date(); 80 now.setTime(System.currentTimeMillis()); 81 //System.out.println(fileName.substring(fileName.lastIndexOf("_")+1,fileName.length())+"-+"); 82 fileName=fileName+sdf.format(now)+".log"; 83 //System.out.println(fileName+"-+"); 84 if(fileName != null) 85 { 86 try 87 { 88 setFile(fileName, fileAppend, bufferedIO, bufferSize); 89 } 90 catch(IOException e) 91 { 92 errorHandler.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e, 4); 93 } 94 } else 95 { 96 LogLog.warn("File option not set for appender [" + name + "]."); 97 LogLog.warn("Are you using FileAppender instead of ConsoleAppender?"); 98 } 99 } 100 101 protected void closeFile() 102 { 103 if(qw != null) 104 try 105 { 106 qw.close(); 107 } 108 catch(IOException e) 109 { 110 if(e instanceof InterruptedIOException) 111 Thread.currentThread().interrupt(); 112 LogLog.error("Could not close " + qw, e); 113 } 114 } 115 116 public boolean getBufferedIO() 117 { 118 return bufferedIO; 119 } 120 121 public int getBufferSize() 122 { 123 return bufferSize; 124 } 125 126 public void setAppend(boolean flag) 127 { 128 fileAppend = flag; 129 } 130 131 public void setBufferedIO(boolean bufferedIO) 132 { 133 this.bufferedIO = bufferedIO; 134 if(bufferedIO) 135 immediateFlush = false; 136 } 137 138 public void setBufferSize(int bufferSize) 139 { 140 this.bufferSize = bufferSize; 141 } 142 143 public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) 144 throws IOException 145 { 146 System.out.println("--==--"+fileName); 147 LogLog.debug("setFile called: " + fileName + ", " + append); 148 if(bufferedIO) 149 setImmediateFlush(false); 150 reset(); 151 FileOutputStream ostream = null; 152 try 153 { 154 ostream = new FileOutputStream(fileName, append); 155 } 156 catch(FileNotFoundException ex) 157 { 158 String parentName = (new File(fileName)).getParent(); 159 if(parentName != null) 160 { 161 File parentDir = new File(parentName); 162 if(!parentDir.exists() && parentDir.mkdirs()) 163 ostream = new FileOutputStream(fileName, append); 164 else 165 throw ex; 166 } else 167 { 168 throw ex; 169 } 170 } 171 Writer fw = createWriter(ostream); 172 if(bufferedIO) 173 fw = new BufferedWriter(fw, bufferSize); 174 setQWForFiles(fw); 175 this.fileName = fileName; 176 fileAppend = append; 177 this.bufferedIO = bufferedIO; 178 this.bufferSize = bufferSize; 179 writeHeader(); 180 LogLog.debug("setFile ended"); 181 } 182 183 protected void setQWForFiles(Writer writer) 184 { 185 qw = new QuietWriter(writer, errorHandler); 186 } 187 188 protected void reset() 189 { 190 closeFile(); 191 fileName = null; 192 super.reset(); 193 } 194 195 protected boolean fileAppend; 196 protected String fileName; 197 protected boolean bufferedIO; 198 protected int bufferSize; 199 }
2、修改DailyRollingFileAppender類
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 19 20 package com.gongli.log; 21 22 import java.io.IOException; 23 import java.io.File; 24 import java.io.InterruptedIOException; 25 import java.text.SimpleDateFormat; 26 import java.util.Date; 27 import java.util.GregorianCalendar; 28 import java.util.Calendar; 29 import java.util.TimeZone; 30 import java.util.Locale; 31 32 import org.apache.log4j.Layout; 33 import org.apache.log4j.helpers.LogLog; 34 import org.apache.log4j.spi.LoggingEvent; 35 36 /** 37 DailyRollingFileAppender extends {@link FileAppender} so that the 38 underlying file is rolled over at a user chosen frequency. 39 40 DailyRollingFileAppender has been observed to exhibit 41 synchronization issues and data loss. The log4j extras 42 companion includes alternatives which should be considered 43 for new deployments and which are discussed in the documentation 44 for org.apache.log4j.rolling.RollingFileAppender. 45 46 <p>The rolling schedule is specified by the <b>DatePattern</b> 47 option. This pattern should follow the {@link SimpleDateFormat} 48 conventions. In particular, you <em>must</em> escape literal text 49 within a pair of single quotes. A formatted version of the date 50 pattern is used as the suffix for the rolled file name. 51 52 <p>For example, if the <b>File</b> option is set to 53 <code>/foo/bar.log</code> and the <b>DatePattern</b> set to 54 <code>'.'yyyy-MM-dd</code>, on 2001-02-16 at midnight, the logging 55 file <code>/foo/bar.log</code> will be copied to 56 <code>/foo/bar.log.2001-02-16</code> and logging for 2001-02-17 57 will continue in <code>/foo/bar.log</code> until it rolls over 58 the next day. 59 60 <p>Is is possible to specify monthly, weekly, half-daily, daily, 61 hourly, or minutely rollover schedules. 62 63 <p><table border="1" cellpadding="2"> 64 <tr> 65 <th>DatePattern</th> 66 <th>Rollover schedule</th> 67 <th>Example</th> 68 69 <tr> 70 <td><code>'.'yyyy-MM</code> 71 <td>Rollover at the beginning of each month</td> 72 73 <td>At midnight of May 31st, 2002 <code>/foo/bar.log</code> will be 74 copied to <code>/foo/bar.log.2002-05</code>. Logging for the month 75 of June will be output to <code>/foo/bar.log</code> until it is 76 also rolled over the next month. 77 78 <tr> 79 <td><code>'.'yyyy-ww</code> 80 81 <td>Rollover at the first day of each week. The first day of the 82 week depends on the locale.</td> 83 84 <td>Assuming the first day of the week is Sunday, on Saturday 85 midnight, June 9th 2002, the file <i>/foo/bar.log</i> will be 86 copied to <i>/foo/bar.log.2002-23</i>. Logging for the 24th week 87 of 2002 will be output to <code>/foo/bar.log</code> until it is 88 rolled over the next week. 89 90 <tr> 91 <td><code>'.'yyyy-MM-dd</code> 92 93 <td>Rollover at midnight each day.</td> 94 95 <td>At midnight, on March 8th, 2002, <code>/foo/bar.log</code> will 96 be copied to <code>/foo/bar.log.2002-03-08</code>. Logging for the 97 9th day of March will be output to <code>/foo/bar.log</code> until 98 it is rolled over the next day. 99 100 <tr> 101 <td><code>'.'yyyy-MM-dd-a</code> 102 103 <td>Rollover at midnight and midday of each day.</td> 104 105 <td>At noon, on March 9th, 2002, <code>/foo/bar.log</code> will be 106 copied to <code>/foo/bar.log.2002-03-09-AM</code>. Logging for the 107 afternoon of the 9th will be output to <code>/foo/bar.log</code> 108 until it is rolled over at midnight. 109 110 <tr> 111 <td><code>'.'yyyy-MM-dd-HH</code> 112 113 <td>Rollover at the top of every hour.</td> 114 115 <td>At approximately 11:00.000 o'clock on March 9th, 2002, 116 <code>/foo/bar.log</code> will be copied to 117 <code>/foo/bar.log.2002-03-09-10</code>. Logging for the 11th hour 118 of the 9th of March will be output to <code>/foo/bar.log</code> 119 until it is rolled over at the beginning of the next hour. 120 121 122 <tr> 123 <td><code>'.'yyyy-MM-dd-HH-mm</code> 124 125 <td>Rollover at the beginning of every minute.</td> 126 127 <td>At approximately 11:23,000, on March 9th, 2001, 128 <code>/foo/bar.log</code> will be copied to 129 <code>/foo/bar.log.2001-03-09-10-22</code>. Logging for the minute 130 of 11:23 (9th of March) will be output to 131 <code>/foo/bar.log</code> until it is rolled over the next minute. 132 133 </table> 134 135 <p>Do not use the colon ":" character in anywhere in the 136 <b>DatePattern</b> option. The text before the colon is interpeted 137 as the protocol specificaion of a URL which is probably not what 138 you want. 139 140 141 @author Eirik Lygre 142 @author Ceki Gülcü*/ 143 public class DailyRollingFileAppender extends FileAppender { 144 145 146 // The code assumes that the following constants are in a increasing 147 // sequence. 148 static final int TOP_OF_TROUBLE=-1; 149 static final int TOP_OF_MINUTE = 0; 150 static final int TOP_OF_HOUR = 1; 151 static final int HALF_DAY = 2; 152 static final int TOP_OF_DAY = 3; 153 static final int TOP_OF_WEEK = 4; 154 static final int TOP_OF_MONTH = 5; 155 156 157 /** 158 The date pattern. By default, the pattern is set to 159 "'.'yyyy-MM-dd" meaning daily rollover. 160 */ 161 private String datePattern = "'.'yyyy-MM-dd"; 162 163 /** 164 The log file will be renamed to the value of the 165 scheduledFilename variable when the next interval is entered. For 166 example, if the rollover period is one hour, the log file will be 167 renamed to the value of "scheduledFilename" at the beginning of 168 the next hour. 169 170 The precise time when a rollover occurs depends on logging 171 activity. 172 */ 173 private String scheduledFilename; 174 175 /** 176 The next time we estimate a rollover should occur. */ 177 private long nextCheck = System.currentTimeMillis () - 1; 178 179 Date now = new Date(); 180 181 SimpleDateFormat sdf; 182 183 RollingCalendar rc = new RollingCalendar(); 184 185 int checkPeriod = TOP_OF_TROUBLE; 186 187 // The gmtTimeZone is used only in computeCheckPeriod() method. 188 static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT"); 189 190 191 /** 192 The default constructor does nothing. */ 193 public DailyRollingFileAppender() { 194 } 195 196 /** 197 Instantiate a <code>DailyRollingFileAppender</code> and open the 198 file designated by <code>filename</code>. The opened filename will 199 become the ouput destination for this appender. 200 201 */ 202 public DailyRollingFileAppender (Layout layout, String filename, 203 String datePattern) throws IOException { 204 super(layout, filename, true); 205 this.datePattern = datePattern; 206 activateOptions(); 207 } 208 209 /** 210 The <b>DatePattern</b> takes a string in the same format as 211 expected by {@link SimpleDateFormat}. This options determines the 212 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 //System.out.println(fileName+"+++___==="); 233 // if("".equals(fileName.substring(fileName.lastIndexOf("_")+1,fileName.length()))){ 234 scheduledFilename = fileName+sdf.format(new Date(file.lastModified())); 235 //}else{ 236 //scheduledFilename = fileName; 237 //} 238 //System.out.println(scheduledFilename+"+++___==="); 239 } else { 240 LogLog.error("Either File or DatePattern options are not set for appender [" 241 +name+"]."); 242 } 243 } 244 245 void printPeriodicity(int type) { 246 switch(type) { 247 case TOP_OF_MINUTE: 248 LogLog.debug("Appender ["+name+"] to be rolled every minute."); 249 break; 250 case TOP_OF_HOUR: 251 LogLog.debug("Appender ["+name 252 +"] to be rolled on top of every hour."); 253 break; 254 case HALF_DAY: 255 LogLog.debug("Appender ["+name 256 +"] to be rolled at midday and midnight."); 257 break; 258 case TOP_OF_DAY: 259 LogLog.debug("Appender ["+name 260 +"] to be rolled at midnight."); 261 break; 262 case TOP_OF_WEEK: 263 LogLog.debug("Appender ["+name 264 +"] to be rolled at start of week."); 265 break; 266 case TOP_OF_MONTH: 267 LogLog.debug("Appender ["+name 268 +"] to be rolled at start of every month."); 269 break; 270 default: 271 LogLog.warn("Unknown periodicity for appender ["+name+"]."); 272 } 273 } 274 275 276 // This method computes the roll over period by looping over the 277 // periods, starting with the shortest, and stopping when the r0 is 278 // different from from r1, where r0 is the epoch formatted according 279 // the datePattern (supplied by the user) and r1 is the 280 // epoch+nextMillis(i) formatted according to datePattern. All date 281 // formatting is done in GMT and not local format because the test 282 // logic is based on comparisons relative to 1970-01-01 00:00:00 283 // GMT (the epoch). 284 285 int computeCheckPeriod() { 286 RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault()); 287 // set sate to 1970-01-01 00:00:00 GMT 288 Date epoch = new Date(0); 289 if(datePattern != null) { 290 for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) { 291 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); 292 simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT 293 String r0 = simpleDateFormat.format(epoch); 294 rollingCalendar.setType(i); 295 Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); 296 String r1 = simpleDateFormat.format(next); 297 //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); 298 if(r0 != null && r1 != null && !r0.equals(r1)) { 299 return i; 300 } 301 } 302 } 303 return TOP_OF_TROUBLE; // Deliberately head for trouble... 304 } 305 306 /** 307 Rollover the current file to a new file.這個第二天生成前一天以日期命名的方法沒用了 308 */ 309 // void rollOver() throws IOException { 310 // 311 // /* Compute filename, but only if datePattern is specified */ 312 // if (datePattern == null) { 313 // errorHandler.error("Missing DatePattern option in rollOver()."); 314 // return; 315 // } 316 // 317 // String datedFilename = fileName+sdf.format(now); 318 // // It is too early to roll over because we are still within the 319 // // bounds of the current interval. Rollover will occur once the 320 // // next interval is reached. 321 // if (scheduledFilename.equals(datedFilename)) { 322 // return; 323 // } 324 // 325 // // close current file, and rename it to datedFilename 326 // this.closeFile(); 327 // 328 // File target = new File(scheduledFilename); 329 // if (target.exists()) { 330 // target.delete(); 331 // } 332 // 333 // File file = new File(fileName); 334 // boolean result = file.renameTo(target); 335 // if(result) { 336 // LogLog.debug(fileName +" -> "+ scheduledFilename); 337 // } else { 338 // LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"]."); 339 // } 340 // 341 // try { 342 // // This will also close the file. This is OK since multiple 343 // // close operations are safe. 344 // this.setFile(fileName, true, this.bufferedIO, this.bufferSize); 345 // } 346 // catch(IOException e) { 347 // errorHandler.error("setFile("+fileName+", true) call failed."); 348 // } 349 // scheduledFilename = datedFilename; 350 // } 351 352 /** 353 * This method differentiates DailyRollingFileAppender from its 354 * super class. 355 * 356 * <p>Before actually logging, this method will check whether it is 357 * time to do a rollover. If it is, it will schedule the next 358 * rollover time and then rollover. 359 * */ 360 // protected void subAppend(LoggingEvent event) { 361 // long n = System.currentTimeMillis(); 362 // if (n >= nextCheck) { 363 // now.setTime(n); 364 // nextCheck = rc.getNextCheckMillis(now); 365 // try { 366 // rollOver(); 367 // } 368 // catch(IOException ioe) { 369 // if (ioe instanceof InterruptedIOException) { 370 // Thread.currentThread().interrupt(); 371 // } 372 // LogLog.error("rollOver() failed.", ioe); 373 // } 374 // } 375 // super.subAppend(event); 376 // } 377 } 378 379 /** 380 * RollingCalendar is a helper class to DailyRollingFileAppender. 381 * Given a periodicity type and the current time, it computes the 382 * start of the next interval. 383 * */ 384 class RollingCalendar extends GregorianCalendar { 385 private static final long serialVersionUID = -3560331770601814177L; 386 387 int type = DailyRollingFileAppender.TOP_OF_TROUBLE; 388 389 RollingCalendar() { 390 super(); 391 } 392 393 RollingCalendar(TimeZone tz, Locale locale) { 394 super(tz, locale); 395 } 396 397 void setType(int type) { 398 this.type = type; 399 } 400 401 public long getNextCheckMillis(Date now) { 402 return getNextCheckDate(now).getTime(); 403 } 404 405 public Date getNextCheckDate(Date now) { 406 this.setTime(now); 407 408 switch(type) { 409 case DailyRollingFileAppender.TOP_OF_MINUTE: 410 this.set(Calendar.SECOND, 0); 411 this.set(Calendar.MILLISECOND, 0); 412 this.add(Calendar.MINUTE, 1); 413 break; 414 case DailyRollingFileAppender.TOP_OF_HOUR: 415 this.set(Calendar.MINUTE, 0); 416 this.set(Calendar.SECOND, 0); 417 this.set(Calendar.MILLISECOND, 0); 418 this.add(Calendar.HOUR_OF_DAY, 1); 419 break; 420 case DailyRollingFileAppender.HALF_DAY: 421 this.set(Calendar.MINUTE, 0); 422 this.set(Calendar.SECOND, 0); 423 this.set(Calendar.MILLISECOND, 0); 424 int hour = get(Calendar.HOUR_OF_DAY); 425 if(hour < 12) { 426 this.set(Calendar.HOUR_OF_DAY, 12); 427 } else { 428 this.set(Calendar.HOUR_OF_DAY, 0); 429 this.add(Calendar.DAY_OF_MONTH, 1); 430 } 431 break; 432 case DailyRollingFileAppender.TOP_OF_DAY: 433 this.set(Calendar.HOUR_OF_DAY, 0); 434 this.set(Calendar.MINUTE, 0); 435 this.set(Calendar.SECOND, 0); 436 this.set(Calendar.MILLISECOND, 0); 437 this.add(Calendar.DATE, 1); 438 break; 439 case DailyRollingFileAppender.TOP_OF_WEEK: 440 this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek()); 441 this.set(Calendar.HOUR_OF_DAY, 0); 442 this.set(Calendar.MINUTE, 0); 443 this.set(Calendar.SECOND, 0); 444 this.set(Calendar.MILLISECOND, 0); 445 this.add(Calendar.WEEK_OF_YEAR, 1); 446 break; 447 case DailyRollingFileAppender.TOP_OF_MONTH: 448 this.set(Calendar.DATE, 1); 449 this.set(Calendar.HOUR_OF_DAY, 0); 450 this.set(Calendar.MINUTE, 0); 451 this.set(Calendar.SECOND, 0); 452 this.set(Calendar.MILLISECOND, 0); 453 this.add(Calendar.MONTH, 1); 454 break; 455 default: 456 throw new IllegalStateException("Unknown periodicity type."); 457 } 458 return getTime(); 459 } 460 }
3、log4j.properties配置
1 #the destination is file 2 log4j.appender.dailyFile=com.gongli.log.DailyRollingFileAppender 3 log4j.appender.dailyFile.File=E:/logs/log_ 4 #log4j.appender.dailyFile.DatePattern=yyyy-MM-dd'.log'這個千萬別配置了,我在源碼里面修改過,生成日志時時間戳會自動附加上的 5 log4j.appender.dailyFile.layout=org.apache.log4j.PatternLayout 6 log4j.appender.dailyFile.layout.ConversionPattern=%d{HH:mm:ss} %5p%6.6r - %m%n
