记一次log4j日志写到错误文件的问题
来源:互联网 发布:淘宝一口价设置技巧 编辑:程序博客网 时间:2024/05/18 00:51
背景
项目采用log4j2记录日志,其中WARN级别日志单独记录,日志文件采用RollingFileAppender,每天一个日志文件。
问题现象
在10月20号的日志文件中,发现了一条10月22日生成的日志文件,导致排查问题时漏看了一条日志。
代码分析
通过查看log4j相关资料,发现RollingFileAppender在记录日志前,会进行一次是否切换文件的判断。
@Override public void append(final LogEvent event) { getManager().checkRollover(event); super.append(event); } public synchronized void checkRollover(final LogEvent event) { if (triggeringPolicy.isTriggeringEvent(event)) { rollover(); } }
checkRollover会进行是否切换文件判断,并进行文件切换,triggeringPolicy使用策略模式实现。在RollingFileAppender里,使用的是TimeBasedTriggeringPolicy,该实现按配置的时间表达式进行日志切换和回滚。
isTriggeringEvent方法中代码如下
@Override public boolean isTriggeringEvent(final LogEvent event) { // 该方法对文件大小进行了判断,如果之前文件没有内容,则不会切换文件。 if (manager.getFileSize() == 0) { return false; } final long nowMillis = event.getTimeMillis(); if (nowMillis >= nextRolloverMillis) { nextRolloverMillis = ThreadLocalRandom.current().nextLong(0, 1 + maxRandomDelayMillis) + manager.getPatternProcessor().getNextTime(nowMillis, interval, modulate); return true; } return false; }
if (manager.getFileSize() == 0) 该判断会进行空文件判断,如果文件是空,则不会切换文件,也即是该bug产生的原因。
问题原因分析
bug产生的过程如下
1. 系统在20号启动,启动后log4j会自动创建一个空文件,如WARN.log
2. 20号和21号均没有WARN日志产生。
3. 22号产生一个WARN日志,日志记录前,先进行是否更换文件判断,由于文件内容为空,所以判断不需要切换文件,日志记录在WARN.log文件中。
4. 第二条WARN日志产生时,文件中有内容,于是进行nextRolloverMillis的判断,但文件是20号生产,并且文件按天切换,所以判断时nowMillis >= nextRolloverMillis 满足条件,triggeringPolicy.isTriggeringEvent
方法返回true,进行rollover()操作,切换文件。
5. 切换文件时,会根据上一次切换时间生成文件名,于是会将WARN.log变化为WARN.log-2017-10-20。并且该文件中有一条22号生成的日志。
问题解决方式
从问题原因中可以发现,在不修改log4j源码的情况下,只需要保证日志文件不为空,即可规避该问题。在rollover的代码中发现以下代码片段
public synchronized void rollover() { if (!hasOutputStream()) { return; } if (rollover(rolloverStrategy)) { try { size = 0; initialTime = System.currentTimeMillis(); createFileAfterRollover(); } catch (final IOException e) { logError("Failed to create file after rollover", e); } } } protected void createFileAfterRollover() throws IOException { setOutputStream(createOutputStream()); }protected void setOutputStream(final OutputStream os) { final byte[] header = layout.getHeader(); if (header != null) { try { os.write(header, 0, header.length); this.outputStream = os; // only update field if os.write() succeeded } catch (final IOException ioe) { logError("Unable to write header", ioe); } } else { this.outputStream = os; } }
文件切换完毕,创建新的文件流时会执行os.write(header, 0, header.length)方法,即会先写入文件头,通过查看layout.getHeader,发现只需在log4j的PatternLayout节点中,配置header属性,即可在创建日志时写入日志文件头。从而很好的规避日志记录到错误的文件中的问题。
- 记一次log4j日志写到错误文件的问题
- 使用Log4j.jar写日志到文件
- 记一次颇有意思的Log4j超大日志问题定位
- 记一次log4j日志文件小事故
- Log4j.jar不同的日志分别写到不同的日志文件
- storm写log4j的日志发现不能写到文件里
- log4j 写错误等告警日志到syslog中
- 使用hibernate 3 + p6spy+ log4j 写日志文件时的错误
- 一次android错误日志的分析问题
- 配置LOG4J的日志文件位置问题
- log4j日志文件乱码问题的解决方法
- log4j日志文件乱码问题的解决方法
- log4j日志文件的目录问题
- log4j日志文件乱码问题的解决方法
- log4j日志文件乱码问题的解决方法
- log4j日志文件乱码问题的解决方法
- log4j日志文件乱码问题的解决方法
- Java Web - 日志-log4j(一)--写到控制台、写到log文件
- ch04_使用多个映射的例子
- 归并排序
- SetACL Studio [windows权限操作工具][翻译]
- PAT Basic 1004
- hdu 1222 Wolf and Rabbit(gcd)
- 记一次log4j日志写到错误文件的问题
- 谱聚类
- 小结2
- Mindjet MindManager 2018 中文破解版下载及新功能介绍
- vue.js或js实现中文A-Z排序
- java并发编程
- ARM定义特殊寄存器(*(volatile unsigned long *))的理解
- 【廖雪峰Python习题集】调用函数
- 【C++】反转单链表(面试的时候屡次被问到)