Log4j的扩展-支持设置最大日志数量的DailyRollingFileAppender

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()方法里面加上删除过多的日志就不行了吗,的确可以这么做:

 

  1 package org.apache.log4j;
  2 
  3 import org.slf4j.Logger;
  4 import org.slf4j.LoggerFactory;
  5 
  6 import java.io.File;
  7 import java.io.FileFilter;
  8 import java.io.IOException;
  9 import java.text.ParseException;
 10 import java.util.*;
 11 
 12 public class MyDailyRollingFileAppender extends DailyRollingFileAppender {
 13     private static Logger logger = LoggerFactory.getLogger(MyDailyRollingFileAppender.class);
 14     private int maxFileSize = 60;
 15 
 16 
 17     void rollOver() throws IOException {
 18         super.rollOver();
 19 
 20         logger.debug("保留文件数量" + maxFileSize + ",日志文件名称为:" + fileName);
 21         List<File> fileList = getAllLogs();
 22         sortFiles(fileList);
 23         logger.debug(fileList.toString());
 24         deleteOvermuch(fileList);
 25     }
 26 
 27     /**
 28      * 删除过多的文件
 29      * @param fileList 所有日志文件
 30      */
 31     private void deleteOvermuch(List<File> fileList) {
 32         if (fileList.size() > maxFileSize) {
 33             for (int i = 0;i < fileList.size() - maxFileSize;i++) {
 34                 fileList.get(i).delete();
 35                 logger.debug("删除日志" + fileList.get(i));
 36             }
 37         }
 38     }
 39 
 40     /**
 41      * 根据文件名称上的特定格式的时间排序日志文件
 42      * @param fileList
 43      */
 44     private void sortFiles(List<File> fileList) {
 45         Collections.sort(fileList, new Comparator<File>() {
 46             public int compare(File o1, File o2) {
 47                 try {
 48                     if (getDateStr(o1).isEmpty()) {
 49                         return 1;
 50                     }
 51                     Date date1 = sdf.parse(getDateStr(o1));
 52 
 53                     if (getDateStr(o2).isEmpty()) {
 54                         return -1;
 55                     }
 56                     Date date2 = sdf.parse(getDateStr(o2));
 57 
 58                     if (date1.getTime() > date2.getTime()) {
 59                         return 1;
 60                     } else if (date1.getTime() < date2.getTime()) {
 61                         return -1;
 62                     }
 63                 } catch (ParseException e) {
 64                     logger.error("", e);
 65                 }
 66                 return 0;
 67             }
 68         });
 69     }
 70 
 71     private String getDateStr(File file) {
 72         if (file == null) {
 73             return "null";
 74         }
 75         return file.getName().replaceAll(new File(fileName).getName(), "");
 76     }
 77 
 78     /**
 79      *  获取所有日志文件,只有文件名符合DatePattern格式的才为日志文件
 80      * @return
 81      */
 82     private List<File> getAllLogs() {
 83         final File file = new File(fileName);
 84         File logPath = file.getParentFile();
 85         if (logPath == null) {
 86             logPath = new File(".");
 87         }
 88 
 89         File files[] = logPath.listFiles(new FileFilter() {
 90             public boolean accept(File pathname) {
 91                 try {
 92                     if (getDateStr(pathname).isEmpty()) {
 93                         return true;
 94                     }
 95                     sdf.parse(getDateStr(pathname));
 96                     return true;
 97                 } catch (ParseException e) {
 98                     logger.error("", e);
 99                     return false;
100                 }
101             }
102         });
103         return Arrays.asList(files);
104     }
105     public int getMaxFileSize() {
106         return maxFileSize;
107     }
108 
109     public void setMaxFileSize(int maxFileSize) {
110         this.maxFileSize = maxFileSize;
111     }
112 }

首先,要注意的就是怎么判断日志文件夹中的日志是否是日志还是另外不相关的文件,比如备份的日志、控制台日志等。我使用的方法就是判断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支持这种通用的配置方法,注意上面配置第四行,不用另外添加其他任何代码。

 

转载于:https://www.cnblogs.com/rembau/p/5201001.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VirtualBox虚拟机安装CentOS 7

新建虚拟机 因为比较简单&#xff0c;所以对于VirtualBox就不做过多介绍了&#xff0c;直接下载安装即可&#xff0c;安装好之后打开Oracle VM VirtualBox管理器&#xff0c;点击新建&#xff0c;选择Red Hat&#xff08;根据windows主机选择 32/64 bit&#xff0c;通常会自动识…

mysql 指定账户已存在_安装mysql时告诉我指定的账户已存在?

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

C语言:用字符读取流和输出流来读写入数据。(文本文件)

/* 文件的几种操作模式: r:只读 w:只写 rw:可读可写 文件的分类&#xff1a; t:文本文件(字符文件) b:二进制文件(字节文件)注意&#xff1a; 采用只读方式打开文件时,如果源文件不存在,打开文件会失败&#xff01; 采用只写方式打开文件时,不管源文件存不存在,都不会失败…

PC 上访问设备数据库的方法

通过 .NET 访问 .sdf 的数据库的方法&#xff1a; 在 VS2005 IDE 中&#xff0c;创建 SQL MOible 数据库&#xff0c;编辑表结果和填充数据。 具体是在 Server Explorer 中&#xff0c;右键单击 “Data Connections”&#xff0c;选择 “Add Connection”&#xff0c;新建一个 …

模板原理和操作数据类的观点【艰难的一天,慢慢的会过去的】

1.模板原理&#xff1a;视图类【将数据输出到模板中&#xff0c;实现对视图的控制】 smarty的类实现对视图的控制【展示和smarty的基本语法&#xff1a;smarty需要它的库进行支持】 面向对象的编程中对象的访问和类的访问本质上还是代码空间的访问&#xff0c;区别也在于对象的…

mysql 用户 类别_从mysql里读取用户类型

##1、后端1(从mysql里读取用户类型)&#xff1a;from django import formsfrom django.forms import widgetsfrom django.forms import fieldsfrom app01 import modelsfrom django.forms import ModelChoiceField,ModelMultipleChoiceFieldfrom django.shortcuts import rende…

从C语言到C++成长经历所得的一些技巧和感悟

我介绍几个办法&#xff0c;学习办法&#xff0c;期望你能找到爱好1。必定要和喜爱编程的&#xff0c;或编程凶猛的&#xff0c;或常常编程的人&#xff0c;在一同&#xff0c;常常探讨问题&#xff01;初学编程会有许多问题呈现&#xff0c;你自己很 难处理 c是我们必定要学的…

老子《道德经》第三十三章

上德不德&#xff0c;是以有德&#xff1b;下德不失德&#xff0c;是以无德。 上德无为而无不为&#xff0c;下德为之而有以为&#xff0c;上仁为之而无以为&#xff0c;上义为之而有以为。 上礼为之而莫之应&#xff0c;则攘臂而扔之。 故失道而后德&#xff0c;失德而后仁&am…

[Spring]-各种标注-零配置

个人学习笔记&#xff0c;记录了一些比较基础的标注&#xff1b; 1、controller 控制器&#xff08;注入服务&#xff09;2、service 服务&#xff08;注入dao&#xff09;3、repository dao&#xff08;实现dao访问&#xff09;4、component pojo实例化到spring容器中&#xf…

mysql弄丢初始密码_MySql密码丢失

windows下mysql密码忘记了第一步&#xff1a;netstat -nat(可以查看mysql是否启动了&#xff0c;如果启动了&#xff0c;可以用输入net stop mysql(或者通过任务管理器结束进程))第二步&#xff1a;mysqld --skip-grant-tables&#xff0c;不要关闭窗口第三步&#xff1a;开启一…

CodeForces-500C

传送门 给n本不同重量的一摞书编号1&#xff5e;n。给定m次操作。操作b代表花费标号为b的书上方其他书的重量总和&#xff0c;将书b位移到这叠书的最上方。问初始书应该如何叠放&#xff0c;才能使m次操作后总花费最小 输入 n本书 m次操作 n个数 书的重量 m个数 操作对象 输出 …

java基础篇---网络编程(UDP程序设计)

UDP程序设计 在TCP的索引操作都必须建立可靠地连接&#xff0c;这样一来肯定会浪费大量的系统性能&#xff0c;为了减少这种开销&#xff0c;在网络中又提供了另外一种传输协议---UDP,不可靠的连接&#xff0c;这种协议在各个聊天工具中被广泛的应用。 咋UDP开发中使用Datagram…

bzoj - 2038: [2009国家集训队]小Z的袜子(hose)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id2038 莫队算法可以解决一类不修改、离线查询问题。而这题可以用莫队来做。 *我是看这个论文学会的&#xff1a;&#xff08;链接~&#xff09; 其实莫队就是一种优化的暴力&#xff0c;只是把查询都离线预先按照规则…

c++ 静态变量赋值_Python变量及常量解释说明

变量(1)在计算机程序中,变量不仅可以是数字,还可以是任意数据类型,变量子啊程序中就是一个变量名表示的,变量名必须是大小写英文,数字,和"_"的组合,切不能以数字开头.a 1 #变量a是一个整数1b "shuai" #变量b是一个字符串1c True #变量c是一个布尔值Tru…

Hibernate中session的clear(),flush(),evict()方法详解

2019独角兽企业重金招聘Python工程师标准>>> 一、Clear 方法 无论是Load 还是 Get 都会首先查找缓存&#xff08;一级缓存&#xff09; 如果没有&#xff0c;才会去数据库查找&#xff0c;调用Clear() 方法&#xff0c;可以强制清除Session缓存。例&#xff1a; pub…

快速排序和折半查找

package BinarySerach;import java.util.Scanner;public class BinarySerch {/***折半查找和快速排序*/static final int N 15;static void quickSort(int [] array,int left,int right){int f,t;int ltemp left;int rtemp right;//确定分界值f array[(leftright)/2];while(…

CANVAS运用-对图片的压缩上传(仅针对移动浏览器)

最近在移动端设计头像上传功能时&#xff0c;原本是以<input type"file">直接通过formData上传&#xff0c;然而实际使用情况是&#xff1a;对于过大的图片&#xff08;高像素手机所拍摄的照片等&#xff09;上传时间过长会导致上传失败&#xff0c;而每次都上…

mysql重命名数据表称方式_在MySQL中,使用()重命名数据表。_学小易找答案

【单选题】( )的上海文坛被称为“张爱玲年”。【多选题】下列哪些是属于共集放大电路的特点?()【阅读理解】Passage Two Thailand is to ban smoking on some of the country’s most popular tourist beaches, with the prospect of up to a year in prison for those caught…

40_自定义泛型方法及其应用

java的泛型不同于C的模板方法那么强大。java的泛型只停留在编译阶段&#xff0c;编译通过后泛型特征被擦除&#xff0c;主要因为保证jvm的效率。 用泛型知识&#xff0c;写一个交换数组元素的方法&#xff08;此方法只适合于引用类型数组!因为int[]不会自动转为Integer[]!&…

SQL Server代理(11/12):维护计划作业

SQL Server代理是所有实时数据库的核心。代理有很多不明显的用法&#xff0c;因此系统的知识&#xff0c;对于开发人员还是DBA都是有用的。这系列文章会通俗介绍它的很多用法。 在这一系列的上一篇&#xff0c;我们看了使用代理帐户模仿Windows安全上下文完成作业步骤的工作。大…