Log4j准時打印日記及添加模塊名設置裝備擺設的Java代碼實例。本站提示廣大學習愛好者:(Log4j准時打印日記及添加模塊名設置裝備擺設的Java代碼實例)文章只能為提供參考,不一定能成為您想要的結果。以下是Log4j准時打印日記及添加模塊名設置裝備擺設的Java代碼實例正文
設置裝備擺設距離時光,准時打印日記
接到個需求,經由過程log4j准時打印日記,需求描寫以下:須要可以或許准時打印日記,時光距離可配。說到准時,起首想到了DailyRollingFileAppender類,各類准時,依據datePattern,這個可以參考類SimpleDateFormat類,罕見的一些准時設置以下:
經由過程不雅察發明沒有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症結字便可;不外今朝樓主沒有處理假如真要寫,並且寫的速度夠快的情形下能夠喪失日記的情形;
應用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)); } }