国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Log4j定時打印日志及添加模塊名配置的Java代碼實例

2019-11-26 14:40:00
字體:
來源:轉載
供稿:網友

配置間隔時間,定時打印日志
 接到個需求,通過log4j定時打印日志,需求描述如下:需要能夠定時打印日志,時間間隔可配。說到定時,首先想到了DailyRollingFileAppender類,各種定時,根據datePattern,這個可以參考類SimpleDateFormat類,常見的一些定時設置如下:

  • '.'yyyy-MM: 每月 
  • '.'yyyy-ww: 每周  
  • '.'yyyy-MM-dd: 每天 
  • '.'yyyy-MM-dd-a: 每天兩次 
  • '.'yyyy-MM-dd-HH: 每小時 
  • '.'yyyy-MM-dd-HH-mm: 每分鐘  

    通過觀察發現沒有n分鐘類似的日期格式,因此,在DailyRollingFileAppender類基礎上進行自定義類的編寫。過程如下:

  1)拷貝DailyRollingFileAppender類源碼并并改名MinuteRollingAppender,為了在log4j.xml中配置,增加配置項intervalTime并添加set、get方法;

private int intervalTime = 10; 

  2)由于DailyRollingFileAppender類使用了RollingCalendar類來計算下一次間隔時間,而需要傳遞參數intervalTime,因此修改RollingCalendar類為內部類;由于其方法就是根據datePattern來計算下一次rollOver動作的時間,此時不需要其他的時間模式,修改方法如下:

public Date getNextCheckDate(Date now) {  this.setTime(now);  this.set(Calendar.SECOND, 0);  this.set(Calendar.MILLISECOND, 0);  this.add(Calendar.MINUTE, intervalTime);  return getTime(); } 

  3)按照分鐘可配時,時間模式就需要禁用了,將其改為static final,響應的去掉其get、set方法和MinuteRollingAppender構造函數中的datePattern參數

private static String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'"; 

    同樣,服務于多種datePattern的方法computeCheckPeriod()也可以刪除; 至此改造就完成了,成品類如下:

package net.csdn.blog;  import java.io.File; import java.io.IOException; import java.io.InterruptedIOException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar;  import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.spi.LoggingEvent;  /**  * 按分鐘可配置定時appender  *  * @author coder_xia  *  */ public class MinuteRollingAppender extends FileAppender {  /**   * The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd"   * meaning daily rollover.   */  private static String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'";  /**   * 間隔時間,單位:分鐘   */  private int intervalTime = 10;   /**   * The log file will be renamed to the value of the scheduledFilename   * variable when the next interval is entered. For example, if the rollover   * period is one hour, the log file will be renamed to the value of   * "scheduledFilename" at the beginning of the next hour.   *   * The precise time when a rollover occurs depends on logging activity.   */  private String scheduledFilename;   /**   * The next time we estimate a rollover should occur.   */  private long nextCheck = System.currentTimeMillis() - 1;   Date now = new Date();   SimpleDateFormat sdf;   RollingCalendar rc = new RollingCalendar();   /**   * The default constructor does nothing.   */  public MinuteRollingAppender()  {  }   /**   * Instantiate a <code>MinuteRollingAppender</code> and open the file   * designated by <code>filename</code>. The opened filename will become the   * ouput destination for this appender.   */  public MinuteRollingAppender(Layout layout, String filename)    throws IOException  {   super(layout, filename, true);   activateOptions();  }   /**   * @return the intervalTime   */  public int getIntervalTime()  {   return intervalTime;  }   /**   * @param intervalTime   *   the intervalTime to set   */  public void setIntervalTime(int intervalTime)  {   this.intervalTime = intervalTime;  }   @Override  public void activateOptions()  {   super.activateOptions();   if (fileName != null)   {    now.setTime(System.currentTimeMillis());    sdf = new SimpleDateFormat(DATEPATTERN);    File file = new File(fileName);    scheduledFilename = fileName      + sdf.format(new Date(file.lastModified()));    }   else   {    LogLog      .error("Either File or DatePattern options are not set for appender ["        + name + "].");   }  }   /**   * Rollover the current file to a new file.   */  void rollOver() throws IOException  {   String datedFilename = fileName + sdf.format(now);   // It is too early to roll over because we are still within the   // bounds of the current interval. Rollover will occur once the   // next interval is reached.   if (scheduledFilename.equals(datedFilename))   {    return;   }    // close current file, and rename it to datedFilename   this.closeFile();    File target = new File(scheduledFilename);   if (target.exists())   {    target.delete();   }    File file = new File(fileName);   boolean result = file.renameTo(target);   if (result)   {    LogLog.debug(fileName + " -> " + scheduledFilename);   }   else   {    LogLog.error("Failed to rename [" + fileName + "] to ["      + scheduledFilename + "].");   }    try   {    // This will also close the file. This is OK since multiple    // close operations are safe.    this.setFile(fileName, true, this.bufferedIO, this.bufferSize);   }   catch (IOException e)   {    errorHandler.error("setFile(" + fileName + ", true) call failed.");   }   scheduledFilename = datedFilename;  }   /**   * This method differentiates MinuteRollingAppender from its super class.   *   * <p>   * Before actually logging, this method will check whether it is time to do   * a rollover. If it is, it will schedule the next rollover time and then   * rollover.   * */  @Override  protected void subAppend(LoggingEvent event)  {   long n = System.currentTimeMillis();   if (n >= nextCheck)   {    now.setTime(n);    nextCheck = rc.getNextCheckMillis(now);    try    {     rollOver();    }    catch (IOException ioe)    {     if (ioe instanceof InterruptedIOException)     {      Thread.currentThread().interrupt();     }     LogLog.error("rollOver() failed.", ioe);    }   }   super.subAppend(event);  }   /**   * RollingCalendar is a helper class to MinuteRollingAppender. Given a   * periodicity type and the current time, it computes the start of the next   * interval.   * */  class RollingCalendar extends GregorianCalendar  {   private static final long serialVersionUID = -3560331770601814177L;    RollingCalendar()   {    super();   }    public long getNextCheckMillis(Date now)   {    return getNextCheckDate(now).getTime();   }    public Date getNextCheckDate(Date now)   {    this.setTime(now);    this.set(Calendar.SECOND, 0);    this.set(Calendar.MILLISECOND, 0);    this.add(Calendar.MINUTE, intervalTime);    return getTime();   }  } } 

測試配置文件如下:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">  <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">   <appender name="myFile" class="net.csdn.blog.MinuteRollingAppender">    <param name="File" value="log4jTest.log" />   <param name="Append" value="true" />   <param name="intervalTime" value="2"/>   <layout class="org.apache.log4j.PatternLayout">    <param name="ConversionPattern" value="%p %d (%c:%L)- %m%n" />   </layout>  </appender>   <root>   <priority value="debug"/>   <appender-ref ref="myFile"/>   </root>  </log4j:configuration> 

      關于定時實現,還可以采用java提供的Timer實現,也就免去了每次記錄日志時計算并且比較時間,區別其實就是自己起個線程與調用rollOver方法,實現如下:

package net.csdn.blog;  import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask;  import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.helpers.LogLog;  public class TimerTaskRollingAppender extends FileAppender {  /**   * The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd"   * meaning daily rollover.   */  private static final String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'";   /**   * 間隔時間,單位:分鐘   */  private int intervalTime = 10;   SimpleDateFormat sdf = new SimpleDateFormat(DATEPATTERN);   /**   * The default constructor does nothing.   */  public TimerTaskRollingAppender()  {  }   /**   * Instantiate a <code>TimerTaskRollingAppender</code> and open the file   * designated by <code>filename</code>. The opened filename will become the   * ouput destination for this appender.   */  public TimerTaskRollingAppender(Layout layout, String filename)    throws IOException  {   super(layout, filename, true);   activateOptions();  }   /**   * @return the intervalTime   */  public int getIntervalTime()  {   return intervalTime;  }   /**   * @param intervalTime   *   the intervalTime to set   */  public void setIntervalTime(int intervalTime)  {   this.intervalTime = intervalTime;  }   @Override  public void activateOptions()  {   super.activateOptions();   Timer timer = new Timer();   timer.schedule(new LogTimerTask(), 1000, intervalTime * 60000);  }   class LogTimerTask extends TimerTask  {   @Override   public void run()   {    String datedFilename = fileName + sdf.format(new Date());    closeFile();    File target = new File(datedFilename);    if (target.exists())     target.delete();    File file = new File(fileName);    boolean result = file.renameTo(target);    if (result)     LogLog.debug(fileName + " -> " + datedFilename);    else     LogLog.error("Failed to rename [" + fileName + "] to ["       + datedFilename + "].");    try    {     setFile(fileName, true, bufferedIO, bufferSize);    }    catch (IOException e)    {     errorHandler.error("setFile(" + fileName       + ", true) call failed.");    }   }  } } 

    不過,以上實現,存在2個問題:

   1)并發

    并發問題可能發生的一個地方在run()中調用closeFile();后,正好subAppend()方法寫日志,此刻文件已關閉,則會報以下錯誤:

java.io.IOException: Stream closed  at sun.nio.cs.StreamEncoder.ensureOpen(Unknown Source)  at sun.nio.cs.StreamEncoder.write(Unknown Source)  at sun.nio.cs.StreamEncoder.write(Unknown Source)  at java.io.OutputStreamWriter.write(Unknown Source)  at java.io.Writer.write(Unknown Source) .............................. 
   解決方法比較簡單,直接讓整個run()方法為同步的,加上synchronized關鍵字即可;不過目前樓主沒有解決如果真要寫,而且寫的速度夠快的情況下可能丟失日志的情況;
   2)性能

    使用Timer實現比較簡單,但是Timer里面的任務如果執行時間太長,會獨占Timer對象,使得后面的任務無法幾時的執行,解決方法也比較簡單,采用線程池版定時器類ScheduledExecutorService,實現如下:

/**  *  */ package net.csdn.blog;  import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;  import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.helpers.LogLog;  /**  * @author coder_xia  *   <p>  *   采用ScheduledExecutorService實現定時配置打印日志  *   <p>  *  */ public class ScheduledExecutorServiceAppender extends FileAppender {  /**   * The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd"   * meaning daily rollover.   */  private static final String DATEPATTERN = "'.'yyyy-MM-dd-HH-mm'.log'";   /**   * 間隔時間,單位:分鐘   */  private int intervalTime = 10;   SimpleDateFormat sdf = new SimpleDateFormat(DATEPATTERN);   /**   * The default constructor does nothing.   */  public ScheduledExecutorServiceAppender()  {  }   /**   * Instantiate a <code>ScheduledExecutorServiceAppender</code> and open the   * file designated by <code>filename</code>. The opened filename will become   * the ouput destination for this appender.   */  public ScheduledExecutorServiceAppender(Layout layout, String filename)    throws IOException  {   super(layout, filename, true);   activateOptions();  }   /**   * @return the intervalTime   */  public int getIntervalTime()  {   return intervalTime;  }   /**   * @param intervalTime   *   the intervalTime to set   */  public void setIntervalTime(int intervalTime)  {   this.intervalTime = intervalTime;  }   @Override  public void activateOptions()  {   super.activateOptions();   Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(     new LogTimerTask(), 1, intervalTime * 60000,     TimeUnit.MILLISECONDS);  }   class LogTimerTask implements Runnable  {   @Override   public void run()   {    String datedFilename = fileName + sdf.format(new Date());    closeFile();    File target = new File(datedFilename);    if (target.exists())     target.delete();    File file = new File(fileName);    boolean result = file.renameTo(target);    if (result)     LogLog.debug(fileName + " -> " + datedFilename);    else     LogLog.error("Failed to rename [" + fileName + "] to ["       + datedFilename + "].");    try    {     setFile(fileName, true, bufferedIO, bufferSize);    }    catch (IOException e)    {     errorHandler.error("setFile(" + fileName       + ", true) call failed.");    }   }  } } 

      關于定時的實現,差不多就到這里了,采用的都默認是10分鐘產生一個新的日志文件,在配置時可以自行設置,不過存在的一個隱患,萬一配置的人不知道時間間隔是分鐘,如果以為是秒,配了個600,又開了debug,產生上G的日志文件,這肯定是個災難,下面的改造就是結合RollingFileAppender的最大大小和最多備份文件個數可配,再次進行完善,下次繼續描述改造過程。

添加模塊名配置
在前面講到了log4j定時打印的定制類實現,就不講指定大小和指定備份文件個數了,從RollingFileAppender類copy代碼到前面的定制類中添加即可,唯一需要解決的是并發問題,即文件關閉rename文件時,發生了記錄日志事件時,會報output stream closed的錯誤。

    現在有這樣一種應用場景,而且經常有:

    1.項目包含有多個不同的工程;

    2.同一工程包含不同的模塊。

    對第一種情況,可以通過配置log4j<catogery=“Test”>,再在產生Logger時使用類似如下方式:

Logger logger=Logger.getLogger("Test"); 

    對第二種情況,我們希望能夠將不同模塊打印到同一個日志文件中,不過希望能夠在日志中打印出模塊名以便出問題時定位問題,因此便有了本文需要的在Appender類中添加配置ModuleName,下面開始改造,與定時打印不同,我們采用RollingFileAppender類為基類進行改造。

    首先,添加配置項moduleName,并增加get、set方法;

    由于繼承自RollingFileAppender,所以只需要在subAppend()中格式化LoggingEvent中的數據,添加formatInfo方法格式化數據,代碼略;

    最終的成品類如下:

package net.csdn.blog;  import org.apache.log4j.Category; import org.apache.log4j.RollingFileAppender; import org.apache.log4j.spi.LoggingEvent;  /**  * @author coder_xia  *  */ public class ModuleAppender extends RollingFileAppender {  private String moduleName;   /**   * @return the moduleName   */  public String getModuleName()  {   return moduleName;  }   /**   * @param moduleName   *   the moduleName to set   */  public void setModuleName(String moduleName)  {   this.moduleName = moduleName;  }   /**   * 格式化打印內容   *   * @param event   *   event   * @return msg   */  private String formatInfo(LoggingEvent event)  {   StringBuilder sb = new StringBuilder();   if (moduleName != null)   {    sb.append(moduleName).append("|");    sb.append(event.getMessage());   }   return sb.toString();  }   @Override  public void subAppend(LoggingEvent event)  {   String msg = formatInfo(event);   super.subAppend(new LoggingEvent(Category.class.getName(), event     .getLogger(), event.getLevel(), msg, null));  } } 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 中阳县| 金昌市| 广昌县| 德钦县| 马鞍山市| 莎车县| 本溪| 元谋县| 托里县| 武山县| 肇东市| 瑞昌市| 弥渡县| 潜江市| 焦作市| 开鲁县| 祁东县| 南涧| 抚松县| 东阳市| 靖宇县| 临夏县| 德阳市| 日土县| 温宿县| 隆安县| 皮山县| 渭源县| 新绛县| 睢宁县| 谷城县| 青冈县| 连州市| 蒙阴县| 屯门区| 邛崃市| 玛多县| 锦州市| 滦南县| 定襄县| 丹江口市|