自定義log4j日志文件命名規(guī)則說(shuō)明
自定義log4j日志文件命名規(guī)則
項(xiàng)目中的日志需要采用一致的命名規(guī)范和文件規(guī)范,命名規(guī)則為:項(xiàng)目模塊標(biāo)識(shí)_index_日期時(shí)間_日志級(jí)別.log,且每個(gè)級(jí)別日志文件放在單獨(dú)的文件夾,且每個(gè)文件夾下日志的數(shù)量不得超過(guò)10個(gè),當(dāng)數(shù)量超過(guò)限制時(shí),刪除相對(duì)較舊的日志,保留較新的日志。
但是發(fā)現(xiàn)log4j并不能滿足此要求,于是
根據(jù)log4j的API定義自己的FileAppender
代碼如下:
package com.dear.simpler.dbrpc.util.log;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
/**;
*
* @author lixiang
* 自定義log文件的命名規(guī)則
*/
public class MyLogFileAppender extends RollingFileAppender {
private long nextRollover = 0;
private static AtomicInteger logIndex = new AtomicInteger(0); //index
public void rollOver() {
File file = null;
if (qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
LogLog.debug("rolling over count=" + size);
// if operation fails, do not roll again until
// maxFileSize more bytes are written
nextRollover = size + maxFileSize;
}
LogLog.debug("maxBackupIndex=" + maxBackupIndex);
if (maxBackupIndex > 0) {
file = new File(getRollingFileName(fileName, logIndex.incrementAndGet()));
if (fileExisted(file)){
file = new File(getRollingFileName(fileName, logIndex.incrementAndGet()));
}
deleteOldFile(file.getParentFile(), maxBackupIndex);
this.closeFile();
}
try {
this.setFile(getRollingFileName(fileName, logIndex.get()), false, bufferedIO, bufferSize);
nextRollover = 0;
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("setFile(" + fileName + ", false) call failed.", e);
}
}
private String getRollingFileName(String fileName, int index) { //使用正則表達(dá)式替代index
Pattern p = Pattern.compile("_\\d+\\_");
Matcher m=p.matcher(fileName);
String str = m.replaceFirst(String.format("_%d_", index));
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); //日期
String dateString = format.format(new Date(System.currentTimeMillis()));
str = str.replaceAll("\\d{14}", dateString);
return str;
}
public synchronized void setFile(String fileName, boolean append, //修改文件名
boolean bufferedIO, int bufferSize) throws IOException {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); //日期
String dateString = format.format(new Date(System.currentTimeMillis()));
String temp = String.format(fileName , dateString); //文件名
super.setFile(temp, append, bufferedIO, bufferSize);
if(append) {
File f = new File(temp);
((CountingQuietWriter)this.qw).setCount(f.length());
}
}
private boolean fileExisted(File file){
boolean res = false;
String[] fts = file.getName().split("_");
File parentFile = file.getParentFile();
for(File f : parentFile.listFiles()){
String[] fns = f.getName().split("_");
if(fns[0].equals(fts[0]) && fns[1].equals(fts[1])){
res = true;
break;
}
}
return res;
}
private void deleteOldFile(File dir , int maxInt){
if(getFileNum(dir) >= maxBackupIndex ){
File[] files = orderByDate(dir);
for (int i = 0; i <= files.length - maxBackupIndex; i++) {
File f = files[i];
f.delete();
}
}
}
private int getFileNum(File file){
return file.list().length;
}
//將文件按日期排序
public File[] orderByDate(File dir) {
File[] fs = dir.listFiles();
Arrays.sort(fs,new Comparator< File>(){
@Override
public int compare(File f1, File f2) {
long diff = f1.lastModified() - f2.lastModified();
if (diff > 0)
return 1;
else if (diff == 0)
return 0;
else
return -1;
}
@Override
public boolean equals(Object obj) {
return true;
}
});
return fs;
}
@Override
protected void subAppend(LoggingEvent event) {
super.subAppend(event);
if (fileName != null && qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
if (size >= maxFileSize && size >= nextRollover) {
rollOver();
}
}
}
}
對(duì)應(yīng)的log4j.properties的配置文件如下
### set log levels ###
log4j.rootLogger = out,E,I
#log4j.logger.com.dear.simpler.dbrpc.util.log.TestUtil=out,D
log4j.appender.D = com.dear.simpler.dbrpc.util.log.MyLogFileAppender
log4j.appender.D.File = ../../logs/db_logs/debug/DB_0_%s_debug.log
log4j.appender.D.Append = true
log4j.appender.D.MaxFileSize=1024MB
log4j.appender.D.MaxBackupIndex=10
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = com.dear.simpler.dbrpc.util.log.ExPatternLayout
log4j.appender.D.layout.ConversionPattern = [%d{yyyy/MM/dd HH:mm:ss,SSS}][%T:%t][%p][%F:%L:%M][%m]%n
log4j.appender.E = com.dear.simpler.dbrpc.util.log.MyLogFileAppender
log4j.appender.E.File = ../../logs/db_logs/error/DB_0_%s_error.log
log4j.appender.E.Append = true
log4j.appender.E.MaxFileSize=10MB
log4j.appender.E.MaxBackupIndex=10
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = com.dear.simpler.dbrpc.util.log.ExPatternLayout
log4j.appender.E.layout.ConversionPattern = [%d{yyyy/MM/dd HH:mm:ss,SSS}][%T:%t][%p][%F:%L:%M][%m]%n
log4j.appender.I = com.dear.simpler.dbrpc.util.log.MyLogFileAppender
log4j.appender.I.File = ../../logs/db_logs/info/DB_0_%s_info.log
log4j.appender.I.Append = true
log4j.appender.I.MaxFileSize=10MB
log4j.appender.I.MaxBackupIndex=10
log4j.appender.I.Threshold = INFO
log4j.appender.I.layout = com.dear.simpler.dbrpc.util.log.ExPatternLayout
log4j.appender.I.layout.ConversionPattern = [%d{yyyy/MM/dd HH:mm:ss,SSS}][%T:%t][%p][%F:%L:%M][%m]%n
輸出的日志文件命名如下

log4j自定義生成文件的名稱
我們?cè)谑褂肔og4j的RollingFileAppender循環(huán)生成文件的時(shí)候,生成的文件的名稱有點(diǎn)兒惡心,例如,文件名稱為app.log,那么生成的文件名依次為app.log.1,app.log.2,....
那么如何去改變生成文件的名稱的規(guī)則呢?下面是一個(gè)簡(jiǎn)單示例:
log4j.properties
log4j.logger.major= INFO, majorMsg
log4j.additivity.logError = false
log4j.appender.majorMsg=com.zws.log.MyRollingFileAppender
log4j.appender.majorMsg.File=${catalina.home}/logs/itc/majorMsg.log
log4j.appender.majorMsg.layout=org.apache.log4j.PatternLayout
log4j.appender.majorMsg.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss}|%p|%C|%M|%L|%m%n
log4j.appender.majorMsg.MaxFileSize=1KB
log4j.appender.majorMsg.MaxBackupIndex=10
MyRollingFileAppender.java
package com.zws.log;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import org.apache.log4j.Priority;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
/**
*
* @author wensh.zhu
*
*/
public class MyRollingFileAppender extends RollingFileAppender {
private long nextRollover = 0;
public void rollOver() {
File target;
File file;
if (qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
nextRollover = size + maxFileSize;
}
LogLog.debug("maxBackupIndex=" + maxBackupIndex);
boolean renameSucceeded = true;
if (maxBackupIndex > 0) {
//刪除序號(hào)最大(最早的文件)的文件
file = new File(genFileName(fileName, maxBackupIndex));
if (file.exists())
renameSucceeded = file.delete();
//所有文件名序號(hào)加1
for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
file = new File(genFileName(fileName, i));
if (file.exists()) {
target = new File(genFileName(fileName, i + 1));
renameSucceeded = file.renameTo(target);
}
}
if (renameSucceeded) {
target = new File(genFileName(fileName, 1));
this.closeFile();
file = new File(fileName);
renameSucceeded = file.renameTo(target);
if (!renameSucceeded) {
try {
this.setFile(fileName, true, bufferedIO, bufferSize);
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("setFile(" + fileName + ", true) call failed.", e);
}
}
}
}
if (renameSucceeded) {
try {
this.setFile(fileName, false, bufferedIO, bufferSize);
nextRollover = 0;
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("setFile(" + fileName + ", false) call failed.", e);
}
}
}
private String genFileName(String name, int index) {
String fileName = "";
if (index > 0) {
String num = index < 10 ? "0" + index : String.valueOf(index);
fileName = name.replace(".log", "") + "_" + num + ".log";
} else {
fileName = name;
}
return fileName;
}
protected void subAppend(LoggingEvent event) {
super.subAppend(event);
if (fileName != null && qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
if (size >= maxFileSize && size >= nextRollover) {
rollOver();
}
}
}
}
以上示例將文件名的生成規(guī)則為:如果文件名為app.log,那么后續(xù)的文件為app_01.log,app_02.log.
僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家
相關(guān)文章
Java實(shí)現(xiàn)單鏈表反轉(zhuǎn)的多種方法總結(jié)
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)單鏈表反轉(zhuǎn)的多種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java實(shí)現(xiàn)從數(shù)據(jù)庫(kù)導(dǎo)出大量數(shù)據(jù)記錄并保存到文件的方法
這篇文章主要介紹了Java實(shí)現(xiàn)從數(shù)據(jù)庫(kù)導(dǎo)出大量數(shù)據(jù)記錄并保存到文件的方法,涉及Java針對(duì)數(shù)據(jù)庫(kù)的讀取及文件寫(xiě)入等操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
Mybatis提示Tag name expected的問(wèn)題及解決
MyBatis是一個(gè)開(kāi)源的Java持久層框架,用于將Java對(duì)象與數(shù)據(jù)庫(kù)表進(jìn)行映射,它提供了一種簡(jiǎn)單、靈活的方式來(lái)訪問(wèn)數(shù)據(jù)庫(kù),同時(shí)也提供了強(qiáng)大的SQL映射和查詢功能2025-01-01
淺談圖片上傳利用request.getInputStream()獲取文件流時(shí)遇到的問(wèn)題
下面小編就為大家?guī)?lái)一篇淺談圖片上傳利用request.getInputStream()獲取文件流時(shí)遇到的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
java UDP通信客戶端與服務(wù)器端實(shí)例分析
這篇文章主要介紹了java UDP通信客戶端與服務(wù)器端,結(jié)合實(shí)例形式分析了java基于UDP通信的客戶端與服務(wù)器端具體實(shí)現(xiàn)技巧及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-01-01
Java利用廣度優(yōu)先搜索實(shí)現(xiàn)抓牛問(wèn)題
廣度優(yōu)先搜索是最簡(jiǎn)便的圖的搜索算法之一,這一算法也是很多重要的圖的算法的原型。本文將利用廣度優(yōu)先搜索實(shí)現(xiàn)抓牛問(wèn)題,感興趣的可以了解下2022-06-06
java基于servlet實(shí)現(xiàn)文件上傳功能解析
這篇文章主要為大家詳細(xì)介紹了java基于servlet實(shí)現(xiàn)上傳功能,后臺(tái)使用java實(shí)現(xiàn),前端主要是js的ajax實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-05-05
Java實(shí)現(xiàn)自定義重試工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了如何基于Java實(shí)現(xiàn)自定義重試工具類(lèi),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11
舉例講解Java的Jackson庫(kù)中ObjectMapper類(lèi)的使用
這篇文章主要介紹了舉例講解Java的Jackson庫(kù)中ObjectMapper類(lèi)的使用,Jackson庫(kù)通常被用來(lái)實(shí)現(xiàn)Java的對(duì)象和JSON之間的轉(zhuǎn)換功能,需要的朋友可以參考下2016-01-01

