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));
}
}