-根据项目需要,log4j在输出日志时,需要进行定制化的格式,由于log4j本身的格式化不能够满足需求,因此需要重写log4j中的方法,修改格式化的方法。而log4j输出文件时,公司的日志标准中规定,需要同时根据时间和大小进行日志文件的输出,并且日志文件名需要明明为特定的格式,因此同时需要重写log4j中的方法,同时满足输出文件的条件和文件名格式的需求。考虑到公司会有其他项目会同时使用到该功能,因此将重写的方法放到了maven的jar项目中,方便其他项目的引用。在引用该jar工程时,需要在原有的工程中的config.properties文件中添加项目名称,即添加projectName=“项目名称”,后面日志输入和日志文件名都会使用。如不添加,则会显示为默认的名称,即“coms”。本工程主要重写了ExPatternParser类中的方法,用于格式化日志输出的格式,和ExRollingFileAppender中的方法,用于格式化输出日志文件名和日志文件分割的标准。以下将会分别讲解ExPatternParser和ExRollingFileAppender中的大体代码逻辑,如有格式化输出日志和特定日志文件名及分割日志文件需求的伙伴,可以下载本项目,修改其中的方法已满足自己的需求。
重写ExPatternParser中方法
根据项目需要,在日志输出时,需要进行特殊的格式化,因log4j原有的格式化不能满足格式化要求,因此对ExPatternParser类进行了重写,定制化格式化要求,格式化后的输出日志格式如下:[*&^%][2018010216:59:02,763][CRM-DEV1-4][1092][15][coms][com.csc.coms.modules.sys.dao.DictDao.findAllList][DEBUG],翻译后的内容如下:[日志记录起始标识][时间][服务器名称][进程号][线程号][系统名称][模块名称][交易流水号(可选)][日志类型][行为类型(可选)][源系统(可选)][目标系统(可选)][报文类(可选)][异步报文号(可选)]。
_**重写finalizeConverter方法,修改输出日志中的占位符,代码如下:**
/**
* 重写finalizeConverter,对特定的占位符进行处理,T表示线程ID占位符,f表示服务器名称占位符,P表示进程号占位符
*/
@Override
protected void finalizeConverter(char c) {
if (c == 'T' || c == 'f' || c == 'P') {
this.addConverter(new ExPatternConverter(this.formattingInfo,c));
}else {
super.finalizeConverter(c);
}
}
_**重写convert方法,修改其中的逻辑,代码如下:**
/**
* 重写方法,增加日志信息的输出格式
* T表示线程号,f表示服务器名称,P表示进程号
*/
@Override
protected String convert(LoggingEvent event) {
String result = null;
if(patterns == 'T'){
result = String.valueOf(Thread.currentThread().getId());
}else if(patterns == 'f'){
String name = ManagementFactory.getRuntimeMXBean().getName();
result = name.split("@")[0];
}else if(patterns == 'P'){
String name = ManagementFactory.getRuntimeMXBean().getName();
result = name.split("@")[1];
}
return result;
}
ExPatternParser类中重写的方法不多,主要就涉及到这两个方法,代码中的注释说明的也比较清楚,因为不进行过多的解释。
重写ExRollingFileAppender类中的方法
根据公司日志标准的需要,第一个需求是在日志输出时,需要同时满足按照日志文件大小、时间、日志类别进行日志文件的分割操作。第二个需求是在日志输出文件时,对文件名需要进行定制化命名规则。为了同时满足这两项需求,因此需要重写ExRollingFileAppender类中相应的方法,由于以上两点需求,导致了原本log4j中删除文件的方法也无法使用,因此在重写ExRollingFileAppender时,也修改了log4j原本删除文件的业务逻辑,考虑到日志输出的性能和可能会影响到日志输出的功能,因此将删除日志功能单独启动了一个线程。日志文件名的格式如下:S0001_INFO_jf-vra-app0001_2017090117_001.log,文件格式翻译后的内容如下:系统名日志类型服务器名称时间日志序列号_识别号。下面将对ExRollingFileAppender类中的方法进行大题的解释。
_**重写subAppend方法,该方法是日志文件分割的主要方法,重写的方法代码如下:**
@Override
protected void subAppend(LoggingEvent event) {
level = event.level.toString();
String nowDate = sdf.format(new Date());
if(StringUtils.isEmpty(projectName)){
LogLog.warn("从配置文件中未获取到projectName项目名称,设置日志文件名的项目名称默认为coms。请在项目中的config.properties里设置projectName属性。");
projectName = "coms";
}
if(fileName.indexOf(separator) == -1){
//新启动项目时,修改日志文件的名称
String fileNames = getFileName(fileName,1);
this.setFile(fileNames);
LogLog.debug("项目启动后文件名称修改为:"+fileName);
}else{
//更新日志文件的名称中的日期时间,否则在获取日志文件名称时会有问题
String fileDate = fileName.substring(0, fileName.lastIndexOf(separator));
fileDate = fileDate.substring(fileDate.lastIndexOf(separator)+1, fileDate.length());
LogLog.debug("修改文件名前的文件名称中的时间为:"+fileDate+",获取到的当前时间为"+nowDate);
if(!nowDate.equals(fileDate)){
String fileNames = getFileName(fileName,1);
this.setFile(fileNames);
LogLog.debug("由于文件名称中的时间不同进行修改,修改后的文件名称为:"+fileName);
}
}
super.subAppend(event);
if (fileName != null && qw != null) {
//获取当前日志文件的大小
long size = ((CountingQuietWriter) qw).getCount();
//获取判断日志文件的时间差是否满足条件
boolean flag = diffDate(fileName);
if ((size >= maxFileSize && size >= nextRollover) || flag) {
LogLog.debug("日志文件进行分割的条件为,当前文件大小:"+size+",文件日期判断结果为:"+flag);
rollOver();
}
}
}
- 重写isAsSevereAsThreshold方法,修改按照日志类型分割文件,代码如下:
/**
* 实现不同级别的日志输出到不同的文件中
*/
@Override
public boolean isAsSevereAsThreshold(Priority priority) {
//只判断是否相等,而不判断优先级
return this.getThreshold().equals(priority);
}
- 重写rollOver方法,实现分割后的日志重命名及删除文件的逻辑,代码如下:
@Override
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) {
//计算当前日志文件夹下的文件数量是否超过阈值,超过则进行删除
countFile(fileName,maxBackupIndex);
// 所有文件名序号加1
for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
file = new File(getFileName(fileName,i));
if (file.exists()) {
target = new File(getFileName(fileName,i+1));
renameSucceeded = file.renameTo(target);
}
}
if (renameSucceeded) {
target = new File(getFileName(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);
}
}
}
- 以上就是对log4j进行定制化重写的大体解释,如需要完整的代码工程,可进行下面的连接进行下载,连接地址为:https://download.csdn.net/download/songyou05/10360993。针对本工程中的代码有更好的建议,可见qq(734108708)进行交流。