- 浏览: 504503 次
- 性别:
- 来自: 深圳
最新评论
-
di1984HIT:
学习了~~
jackson JSON对象映射出多余字段的bug -
lvye351:
当然,在tomcat还有JPDA这种方式 ,来远程debug: ...
配置linux下tomcat的远程debug -
hety163:
好,语言简单明了易懂
Http和Socket连接区别 -
高军威:
<b>行不行</b>
XSS转码 && struts2 property标签的bug -
chjy1983:
请教下,我这样:JSONObject jsonObject = ...
HttpClient4 POST数据及问题
在log4j的大多数appender中,都有maxBackupIndex属性,但是这个DailyRollingFileAppender没有,也就是说它会每天滚一个文件,却没有办法控制文件总个数。这绝对是系统的一个“着火点”,下面就开始动手改造了:
一。研究整个log4j的appender结构:
对框架的一个模块进行扩展,并非总是直接继承某个类就好了,如果不进一步深入研究就有可能掉入某些陷阱。(比如扩展log4j的Logger类,直接继承它并不能得到任何好处,具体解释清参考官方文档。),还好log4j对level,appender,layerout都扩展有很好支持的。
然后就是看log4j的配置文件了。 配置文件是可以直接配置扩展appender属性的,这样就替我们节省了一堆定义、解析、处理的过程
# 给自己的类取个对应的名 log4j.appender.appenderName=fully.qualified.name.of.appender.class #还可以给自己的类property设置值,也就是说扩展的maxBackupIndex属性可以配置 log4j.appender.appenderName.option1=value1 ... log4j.appender.appenderName.optionN=valueN
二。大致胸有成竹后,可以开始看DailyRollingFileAppender的源码了。
直接看属性跟方法结构
大致可以猜出这个类做了如下几个事情:继承了根类appender、支持DatePattern解析并针对DatePattern设置的滚动条件组装filename、实现“监听”方法,到时间点切换logfile。。。 大部分的工作都给我们做好了:)
现在唯一需要改动的就是,“切换文件”方法,在切换新文件的同时,删除掉最老的n个log。
/** Rollover the current file to a new file. */ void rollOver() throws IOException { /* Compute filename, but only if datePattern is specified */ if (datePattern == null) { errorHandler.error("Missing DatePattern option in rollOver()."); return; } 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, false, this.bufferedIO, this.bufferSize); } catch(IOException e) { errorHandler.error("setFile("+fileName+", false) call failed."); } scheduledFilename = datedFilename; }
看到这里就发现问题了,由于DatePattern格式可配置,那么产生的滚动的文件名也是不同的,也没有什么规律可循。
比如".yyyy-ww",是按周滚动,当配置改成".yyyy-MM "按月滚动之后,通过文件名匹配删除旧文件将会导致错误。
另外,日志文件的切换不是定时轮询而是事件促发机制,只有在进行写操作的时候才会去判断是否需要滚动文件!那么写操作在跨过一个滚动周期执行的时候,文件名会产生空缺而不保证连续性。
也许这就是log4j本身没有对这个appender做文件个数限制的原因吧。
三。妥协吧。
框架的功能总是尽量强大的,但使用总是最简单的功能!在IDC环境中通常是不允许按时间滚动记log的,主要是防止日志文件撑爆硬盘成为着火点。 这里考虑启用按时间滚动,主要是性能日志的统计脚本需要日志文件以日期为名按天存储,并且只需要备份前一天的即可.
那么我的需求就简单了:简化功能!
仿造DailyRollingFileAppender实现1.仅支持按天滚动的 、2.格式写死的DatePattern ,3.最大备份文件个数为n的appender 。(备份数可配考虑灵活性,但一定要有参数检查预防万一!)
限制datepattern,一方面可以防止配错,弄成按月滚动肯定死翘翘;另一方面也容易处理MaxBackupIndex删除历史文件。 more,既然知道是按天滚动,check的方法当然可以简化了:
最终修改版的按天滚动appender如下:
package cxxxxxxxj; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.spi.LoggingEvent; /** * 扩展的一个按天滚动的appender类 * 暂时不支持datePattern设置,但是可以配置maxBackupIndex * @author weisong * */ public class DayRollingFileAppender extends FileAppender { /**不允许改写的datepattern */ private final String datePattern = "'.'yyyy-MM-dd"; /**最多文件增长个数*/ private int maxBackupIndex = 2; /**"文件名+上次最后更新时间"*/ private String scheduledFilename; /** The next time we estimate a rollover should occur. */ private long nextCheck = System.currentTimeMillis () - 1; Date now = new Date(); SimpleDateFormat sdf; /** The default constructor does nothing. */ public DayRollingFileAppender() { } /** 改造过的构造器 */ public DayRollingFileAppender (Layout layout, String filename, int maxBackupIndex) throws IOException { super(layout, filename, true); this.maxBackupIndex = maxBackupIndex; activateOptions(); } /** * 初始化本Appender对象的时候调用一次 */ public void activateOptions() { super.activateOptions(); if(fileName != null) { //perf.log now.setTime(System.currentTimeMillis()); sdf = new SimpleDateFormat(datePattern); File file = new File(fileName); //获取最后更新时间拼成的文件名 scheduledFilename = fileName+sdf.format(new Date(file.lastModified())); } else { LogLog.error("File is not set for appender ["+name+"]."); } if(maxBackupIndex<=0) { LogLog.error("maxBackupIndex reset to default value[2],orignal value is:" + maxBackupIndex); maxBackupIndex=2; } } /** 滚动文件的函数: 1.对文件名带的时间戳进行比较,确定是否更新 2.if需要更新,当前文件rename到文件名+日期, 重新开始写文件 3. 针对配置的maxBackupIndex,删除过期的文件 */ void rollOver() throws IOException { String datedFilename = fileName + sdf.format(now); // 如果上次写的日期跟当前日期相同,不需要换文件 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 + "]."); } // 删除过期文件 if (maxBackupIndex > 0) { File folder = new File(file.getParent()); List<String> maxBackupIndexDates = getMaxBackupIndexDates(); for (File ff : folder.listFiles()) { //遍历目录,将日期不在备份范围内的日志删掉 if (ff.getName().startsWith(file.getName()) && !ff.getName().equals(file.getName())) { //获取文件名带的日期时间戳 String markedDate = ff.getName().substring(file.getName().length()); if (!maxBackupIndexDates.contains(markedDate)) { result = ff.delete(); } if (result) { LogLog.debug(ff.getName() + " ->deleted "); } else { LogLog.error("Failed to deleted old DayRollingFileAppender file :" + ff.getName()); } } } } try { // This will also close the file. This is OK since multiple // close operations are safe. this.setFile(fileName, false, this.bufferedIO, this.bufferSize); } catch (IOException e) { errorHandler.error("setFile(" + fileName + ", false) call failed."); } scheduledFilename = datedFilename; // 更新最后更新日期戳 } /** * Actual writing occurs here. 这个方法是写操作真正的执行过程! * */ protected void subAppend(LoggingEvent event) { long n = System.currentTimeMillis(); if (n >= nextCheck) { //在每次写操作前判断一下是否需要滚动文件 now.setTime(n); nextCheck = getNextDayCheckPoint(now); try { rollOver(); } catch (IOException ioe) { LogLog.error("rollOver() failed.", ioe); } } super.subAppend(event); } /** * 获取下一天的时间变更点 * @param now * @return */ long getNextDayCheckPoint(Date now) { Calendar calendar = Calendar.getInstance(); calendar.setTime(now); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0);//注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的 calendar.add(Calendar.DATE, 1); return calendar.getTimeInMillis(); } /** * 根据maxBackupIndex配置的备份文件个数,获取要保留log文件的日期范围集合 * @return list<'fileName+yyyy-MM-dd'> */ List<String> getMaxBackupIndexDates() { List<String> result = new ArrayList<String>(); if(maxBackupIndex>0) { for (int i = 1; i <= maxBackupIndex; i++) { Calendar calendar = Calendar.getInstance(); calendar.setTime(now); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0);//注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的 calendar.add(Calendar.DATE, -i); result.add(sdf.format(calendar.getTime())); } } return result; } public int getMaxBackupIndex() { return maxBackupIndex; } public void setMaxBackupIndex(int maxBackupIndex) { this.maxBackupIndex = maxBackupIndex; } public String getDatePattern() { return datePattern; } // public static void main(String[] args) { // DayRollingFileAppender da = new DayRollingFileAppender(); // da.setMaxBackupIndex(2); // da.sdf = new SimpleDateFormat(da.getDatePattern()); // System.out.println(da.getMaxBackupIndexDates()); // // File f = new File("e:/log/b2c/perf.log"); // System.out.println("f.name=" + f.getName()); // File p = new File(f.getParent()); // for(File ff : p.listFiles()) { // System.out.println(ff); // } // } }
评论
debug之咯,呵呵。代码都写的比较清楚了。 泛泛的讲不能用,本身就不是程序员应有的精神
发表评论
-
Nginx rewrite permanent
2014-03-19 16:43 1714fpm之后,尝试兼容url错误的一段redirect失效。具 ... -
ZmEu漏洞扫描
2014-02-21 16:59 7439挺黑的,nginx抓出来的日志。扫描各种php软件、数据库 ... -
Continuous Integration with Xcode 5
2014-01-09 15:04 963xcode5 及持续集成, 花了20分钟上手配置, 效果非 ... -
Httpclient4.3实例。 每个版本接口变更都巨大
2014-01-08 17:55 33131.新增简单的url请求内容返回, 比较时髦的链调用 ... -
nginx proxy_http_version
2014-01-07 16:10 6220nginx转 apache ,发现HTTP协议版本 从1.1 ... -
ubuntu一键升级到13.10的教训
2013-12-16 11:51 1073从13.04升级到13.10,主要两个变化非常蛋疼: 1 ... -
【PHP】Codeigniter : Unable to locate the model you have specified
2013-12-10 11:36 1879产生这个问题一般两个原因: 1. google到的结果,类 ... -
springMVC + jsonP
2013-11-20 12:58 4159/** * 根据分类id,取新闻列表 ... -
PC端 浏览器Agent切换工具
2013-11-18 11:04 1021插件比较方便,技术流还是推荐fiddler -
Spring3.x中的几个异步执行
2013-08-22 15:00 28631.servlet3 细节可以阅读http://www.i ... -
Mybatis Cache探究
2013-08-22 12:01 1941这里先不讨论第三方的cache集成(有memcach ... -
spring3-基于注解的AOP
2013-08-02 11:58 1009要点: 1.aop的概念真的很多。。。其实从使用出发无非 ... -
HttpClient4 POST数据及问题
2012-05-23 18:03 33523post 方式挂参数的三种格式, mark一下。 ... -
struts2-ognl mark
2011-12-29 16:49 1534暂时mark在这,后面再补充 1. 关于漏洞的问 ... -
类模板语言的变量替换~简易java实现
2011-04-06 15:25 3681场景1:数据库存有 xx,y ... -
XSS转码 && struts2 property标签的bug
2011-03-25 15:36 6612一。了解背景 下面两张图,比较html转义和js的转义。 ... -
小折腾一下swing
2011-03-23 16:23 1177近来看美剧《Lost》,可惜下载的rm文件名太长,很难找到自己 ... -
FileUploadInterceptor ~mark陷阱
2011-03-17 15:45 155803/17 14:25:40 [ERRO ... -
新浪微博技术架构分析-转载
2011-03-07 17:07 2087中国首届微博开发者大会在北京举行,这是国内微博行业的首场技术盛 ... -
谨慎使用SocketChannel的read方法
2011-01-13 18:02 5982下面的代码是一个实例化SocketChannel的过程: ...
相关推荐
log4j_appender 带有MaxBackupIndex的DailyRollingFileAppender
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=c\:\\SampleMessages.html log4j.appender.A1.DatePattern=yyyyMMdd-HH log4j.appender.A1.layout=org.apache.log4j....
这是Log4J DailyRollingFileAppender的修改版本,具有maxBackupIndex,如果超过给定的maxBackupIndex大小,则删除旧的日志文件。
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=SampleMessages.log4j log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j' log4j.appender.A1.layout=org.apache.log4j.xml....
当tomcat的server.xml的配置内包含时 DailyRollingFileAppender 类不能正确重命名。 log4j DailyRollingFileAppender 类不能正常重命名
过了时间将原日志文件命名为原文件名后加上log4j.appender.File.DatePattern='.'yyyy-MM-dd-HH对应格式的日期, 注意不能用:和_。 关于DailyRollingFileAppender的使用,参考:...
log4j中DailyRollingFileAppender删除前N天日志 可配置maxBackupIndex参数 文件中包含测试工程以及源码
包括修改后的[color=darkred]org.apache.log4j.DailyRollingFileAppender[/color]类的源代码和已编译好的文件. 请用DailyRollingFileAppender.class替换log4j-1.2.15.jar包里相应的类. 博文链接:...
在Tomcat6下使用Log4j记录日志,天创建新日志文件时(日志文件设置为:org.apache.log4j.DailyRollingFileAppender)报: log4j:ERROR Failed to rename错误; 网上查找了下原因,大概意思是日志文件始终被占有,所以...
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), org.apache.log4j.WriterAppender(将日志信息...
使用log4j的DailyRollingFileAppender时只有一个日志文件,修改DailyRollingFileAppender源码
针对log4j:ERROR Failed to rename的问题,修改源代码中的DailyRollingFileAppender.java文件,将rename改为copy。 该log4j-1.2.15.jar,就是修改后的jar包。 已经过测试,可以正常生成日志文件。
log4j:ERROR Failed to rename错误解决办法,修改源码里的DailyRollingFileAppender类,用此jar包就不会再出现ERROR Failed to rename的错误了
本工程用于研究log4j日志输出目的地org.apache.log4j.DailyRollingFileAppender的使用方法 本工程编码方式:UTF-8 本工程开发工具:MyEclipse
同时兼具RollingFileAppender 和TimeBasedRollingFileAppender 转存文件的功能。也具备以当前日期作为目录存储日志文件。
完整分析了log4cpp的整体架构,详细介绍了log4cpp的这个重要组件的实现分析了log4cpp内部所使用的设计模式。介绍了log4cpp中的Category的完整实现细节,介绍了所有的Layout及其子类的具体实现。也详细介绍了比较常用...
1.log4j进行日志切分 1)准备三个包:log4j-1.2.17.jar tomcat-juli.jar tomcat-juli-adapters.jar 放到tomcat的lib目录或者是...log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender log4j.appen
原程序,只实现了RollingFileAppender ,文件可以按指定大小 定量产生,但是常用的记录日志方法是 按天生产一个日志文件,所以就从官网上下了源码,简单的修改下,增加了对DailyRollingFileAppender 的支持,可以...