Log4j现在已经被大家熟知了,所有细节都可以在网上查到,Log4j支持Appender,其中DailyRollingFileAppender是被经常用到的Appender之一。在讨论今天的主题之前,我们先看下另外一个Appender。

最常用的Appender——RollingFileAppender

下面是RollingFileAppender的一个Log4j配置样例(配置1):

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.Threshold=DEBUG
log4j.appender.R.File=test.log
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.R.MaxFileSize=20MB
log4j.appender.R.MaxBackupIndex=10

RollingFileAppender使用MaxFileSize设置一个日志文件的最大大小,当产生多个日志时,会在日志名称后面加上".1"、".2"、……这样的后缀,我们可以看到RollingFileAppender有个属性MaxBackupIndex,这个属性通过限制日志文件名后缀".n"中的n大小来限制日志数量,比如上面MaxBackupIndex=10,其实最大日志数量为11。我们知道这个有这个限制是很必要的,当我们的程序在服务器上运行时,随着时间的迁移,日志会越来越多,如果对日志数量没有限制,日志大小会越来越大,最后甚至占满整个硬盘。

可以按照周期时间来滚动日志文件的Appender——DailyRollingFileAppender

下面是DailyRollingFileAppender的一个Log4j配置样例(配置2):

log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File=test.log
log4j.appender.logfile.DataPattern='.'yyyy-MM-dd-HH-mm
log4j.appender.logfile.Threshold=debug
log4j.appender.logfile.encoding=UTF-8
log4j.appender.logfile.Append=false
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern= [%d{yyyy-MM-dd HH\:mm\:ss}]%-5p %c(line\:%L) %x-%m%n

DailyRollingFileAppender特点是固定周期时间生成一个日志文件,比如,默认情况是每天生成一个文件。这种日志可以方便根据时间来定位日志位置,使日志清晰易查。但是这种日志有个不好地方是,不能限制日志数量,MaxBackupIndex属性和MaxFileSize在DailyRollingFileAppender中是无效的,我们上面已经提到限制日志数量的必要性。这里有两个解决办法:

  • linux上crontab+shell
  • java进程里面起一个线程,定期扫描日志文件夹。

但是这两种方法都不是很方便,有没有更好的办法呢?

重写DailyRollingFileAppender——MyDailyRollingFileAppender

查看DailyRollingFileAppender源代码,发现rollOver()方法是用来生成文件的,当调用subAppend()方法时会根据判断当前时间是否大于应该生成新文件的时间了(具体实现可以查看源码,逻辑还是比较清晰的),如果大于,就生成。首先把当前日志重命名,命名格式为test.log.yyyy-MM-dd-HH-mm,然后重新建test.log文件。看到这里我们就可以想,在rollOver()方法里面加上删除过多的日志就不行了吗,的确可以这么做:

 package org.apache.log4j;

 import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.text.ParseException;
import java.util.*; public class MyDailyRollingFileAppender extends DailyRollingFileAppender {
private static Logger logger = LoggerFactory.getLogger(MyDailyRollingFileAppender.class);
private int maxFileSize = 60; void rollOver() throws IOException {
super.rollOver(); logger.debug("保留文件数量" + maxFileSize + ",日志文件名称为:" + fileName);
List<File> fileList = getAllLogs();
sortFiles(fileList);
logger.debug(fileList.toString());
deleteOvermuch(fileList);
} /**
* 删除过多的文件
* @param fileList 所有日志文件
*/
private void deleteOvermuch(List<File> fileList) {
if (fileList.size() > maxFileSize) {
for (int i = 0;i < fileList.size() - maxFileSize;i++) {
fileList.get(i).delete();
logger.debug("删除日志" + fileList.get(i));
}
}
} /**
* 根据文件名称上的特定格式的时间排序日志文件
* @param fileList
*/
private void sortFiles(List<File> fileList) {
Collections.sort(fileList, new Comparator<File>() {
public int compare(File o1, File o2) {
try {
if (getDateStr(o1).isEmpty()) {
return 1;
}
Date date1 = sdf.parse(getDateStr(o1)); if (getDateStr(o2).isEmpty()) {
return -1;
}
Date date2 = sdf.parse(getDateStr(o2)); if (date1.getTime() > date2.getTime()) {
return 1;
} else if (date1.getTime() < date2.getTime()) {
return -1;
}
} catch (ParseException e) {
logger.error("", e);
}
return 0;
}
});
} private String getDateStr(File file) {
if (file == null) {
return "null";
}
return file.getName().replaceAll(new File(fileName).getName(), "");
} /**
* 获取所有日志文件,只有文件名符合DatePattern格式的才为日志文件
* @return
*/
private List<File> getAllLogs() {
final File file = new File(fileName);
File logPath = file.getParentFile();
if (logPath == null) {
logPath = new File(".");
} File files[] = logPath.listFiles(new FileFilter() {
public boolean accept(File pathname) {
try {
if (getDateStr(pathname).isEmpty()) {
return true;
}
sdf.parse(getDateStr(pathname));
return true;
} catch (ParseException e) {
logger.error("", e);
return false;
}
}
});
return Arrays.asList(files);
}
public int getMaxFileSize() {
return maxFileSize;
} public void setMaxFileSize(int maxFileSize) {
this.maxFileSize = maxFileSize;
}
}

首先,要注意的就是怎么判断日志文件夹中的日志是否是日志还是另外不相关的文件,比如备份的日志、控制台日志等。我使用的方法就是判断sdf.parse(name.replaceAll(file.getName(), ""))是否报异常,如果不报异常就说明这个文件是日志,当然不排除有的文件命名恰好符合这个格式,但是这样的文件在日志文件夹下,我们认为它就是一个日志文件也是合理的。然后我们根据sdf.parse(name.replaceAll(file.getName(), ""))解析出来的Date为所有日志进行升序排序放到一个队列中,再保留这个队列最后maxFileSize个文件的情况下,删除多余的日志文件。

然后,我们注意到我们上面的逻辑中用了maxFileSize这个变量,这个变量在MyDailyRollingFileAppender中,这个变量是怎么赋值的呢?

log4j.appender.logfile=org.apache.log4j.MyDailyRollingFileAppender
log4j.appender.logfile.File=test.log
log4j.appender.logfile.DatePattern='.'yyyy-MM-dd-HH-m
log4j.appender.logfile.MaxFileSize=5
log4j.appender.logfile.Append=false
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern= [%d{yyyy-MM-dd HH\:mm\:ss}]%-5p %c(line\:%L) %x-%m%n

其实Log4j支持这种通用的配置方法,注意上面配置第四行,不用另外添加其他任何代码。

Log4j的扩展-支持设置最大日志数量的DailyRollingFileAppender的更多相关文章

  1. Log4j的扩展RollingFileAppender、DailyRollingFileAppender

    最常用的Appender--RollingFileAppender RollingFileAppender的一个Log4j配置样例: log4j.appender.R=org.apache.log4j ...

  2. 基于Log4j完成定时创建和删除日志的方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 Log4j作为常用的日志生成工具,其清除日志的策略却十分有限. ...

  3. Java学习-046-日志抓取合并后排序问题解决方案之 --- log4j 二次定制,实现日志输出添加延时10ms

    自3月25至今,已经好久没有写学习日志了,今天在写日志抓取合并的小方法,发现抓取后的日志并米有依据系统执行的日志顺序排序.日志抓取排列逻辑如下: 通过日志标识,从各个日志文件(例如 use.log,e ...

  4. Log4j配置的经典总结,打印日志文件,日志存库

        一.介绍 Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制 日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务 器.NT的事件记录器.UNIX Sy ...

  5. 日志框架(Log4J、SLF4J、Logback)--日志规范与实践

    文章目录 一.Log4j 1.1新建一个Java工程,导入Log4j包,pom文件中对应的配置代码如下: 1.2resources目录下创建log4j.properties文件. 1.3输出日志 1. ...

  6. wrk 及扩展支持 tcp 字节流协议压测

    wrk 及扩展支持 tcp 字节流协议压测 高性能.方便使用的 HTTP(s) 的流量压测工具,结合了多个开源项目开发而成: redis 的 ae 事件框架 luajit openssl http-p ...

  7. log4j配置文件及nutch中的日志配置

    使用slf4j作为日志系统时,由于slf4j只是一个接口,它需要一个具体实现来执行. 具体参考http://blog.csdn.net/jediael_lu/article/details/43854 ...

  8. Log4j和thymeleaf结合导致sql 日志输出不了

    最近用thymeleaf模板引擎的时候,发现sql 的日志输出不了,普通ssm框架搭建没问题,能输出sql日志,可是在使用thymeleaf引擎后,就发现用不了. 现在找到一种解决方法, 导入jar包 ...

  9. 设置Nginx日志

    在nginx.conf文件或vhosts/*.conf文件中的access_log日志中指定级别为main. http { include mime.types; default_type appli ...

随机推荐

  1. Segment,Path,Ring和Polyline对象

    Segment几何对象   Segment对象是一个有起点和终点的“线“,也就是说Segement只有两个点,至于两点之间的线是直的,还是曲的,需要其余的参数定义.所以Segment是由起点,终点和参 ...

  2. arm nop

    MOV R0,R0 这个语句相当于做一次无用功,也就相当于实现了NOP.       接下去就是怎么修改机器码的部分.先查询手册,查到MOV 的机器码是0xE1A0(此处可能不同,主要看自己IDA反汇 ...

  3. hadoop2.2 datanode 启动不了

    FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed for block pool Block po ...

  4. 使用MyEclipse构建MAVEN项目 - 我的漫漫程序之旅 - BlogJava

    body { font-family: Microsoft YaHei UI,"Microsoft YaHei", Georgia,Helvetica,Arial,sans-ser ...

  5. fragment 数据传递,通信

    Fragment之间的通信   在本节中,你会学到 1.定义接口 2.实现接口 3.将消息传递给fragment 为了重用Fragment UI 组件,在设计中你应该通过定义每一个fragemnt自己 ...

  6. LintCode 11 二叉查找树的搜索区间

    题目链接:http://www.lintcode.com/zh-cn/problem/search-range-in-binary-search-tree/ 1.描述 给定两个值 k1 和 k2(k1 ...

  7. SpringMVC轻松学习-SpringMVC介绍(一)

    Spring  MVC 背景介绍 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 ...

  8. (简单) POJ 1195 Mobile phones,二维树状数组。

    Description Suppose that the fourth generation mobile phone base stations in the Tampere area operat ...

  9. [SQLite]SQL语法

    SQLite常用SQL语句 创建表格 sql="CREATE TABLE IF NOT EXISTS MusicList (id integer primary key AutoIncrem ...

  10. hibernate---性能优化, 1+N问题

    session级缓存 保存一个hashmap, 读出来的对象放在里面, 如果读出来50条放50条, 如果另起session原来的50条就被清除.可以手动session.clear清除. 如果同一个se ...