题记------学习别人的精髓,并加以总结,消化吸收,这就是提高!!!
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