公司的IT运维最近将我们运行了两年的SSH项目上K8S,好处是多副本运行,统一日志管理,无感发布,高可用,在稳定运行了一段时间后,我们某一天发现一个问题需要根据后台输出的日志进行分析时,发现当前正在记录的日志没有问题,但是翻昨天甚至前几天的日志都发现日志的体积很小,与当前的业务量相比特别不合理。
翻看日志发现日志只有小小的几M,而目前正在记录的日志仅半天就已经有十几M了,而且翻看里面的内容也只有凌晨3点到4点一个小时的日志量(输出的日志有带时间)。
之后我找帮我们上了K8S的运维,询问他是否对我们的日志进行了清理,或者是帮我们切割日志时出现了问题。
得到的答复是:没有删除我们的日志,也没有帮我们写脚本进行分割。
为什么第一时间找的是运维呢,因为当时另一个项目是使用Linux的分割日志脚本,所以我想当然地认为这个也是,然而实际上是使用了Log4j的DailyRollingFileAppender进行每日分割
于是我就回去自己的程序里找是哪里进行的日志分割,后来找到了我们的log4j.properties文件,里面的配置是这么写的
log4j.rootLogger=INFO, dailyRollingFile
#dailyRollingFile
log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.Encoding=UTF-8
# 备份的日志用.yyyy-MM-dd来重命名
log4j.appender.dailyRollingFile.DatePattern='.'yyyy-MM-dd
log4j.appender.dailyRollingFile.File=${catalina.home}/logs/web.log
log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] (%c{1}:%L) - %m%n
log4j.logger.noModule=FATAL
配置看起来没有问题,而且也是每天定时按规则分割了日志,那么问题出在了哪里,为什么分割之后前一天的日志就不完整了,然后我去搜索了一下关于Log4j日志分割的原理:
/**
将当前的文件滚动到新文件
*/
void rollOver() throws IOException {
// ...省略前面
// fileName是当前日志名称, 拼接指定格式的当前时间
String datedFilename = fileName+sdf.format(now);
// 如果准备生成滚动日志的时间跟当前时间相等, 则等待下一周期再进行滚动生成, 这个地方有点不太理解是否有必要
if (scheduledFilename.equals(datedFilename)) {
return;
}
this.closeFile();
// 获取一个叫scheduledFilename的文件, 如果该文件已存在则将它删除
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+"].");
}
}
即在生成新文件时,如果文件已存在会删除。那问题就是出现在这里,由于我们的应用存在2个副本,多个副本记录在同一个日志目录下,当应用A到时间滚动日志时,会生成一个当前日期的文件,应用B到时间滚动日志,假如比A慢了一两秒,判断到已经存在这个文件了,就将该文件删除,而被删除的这个文件实际上就是今天的日志。因此导致在第二天我们回看之前的日志时,里面已经没有前一天的内容了
修改日志配置,不通过Log4j中的org.apache.log4j.DailyRollingFileAppender
进行日志滚动备份,而是使用普通的org.apache.log4j.FileAppender
进行日志记录,然后写文件执行脚本(如果是自己的Linux服务器的话就写Linux的脚本),通过在K8S的cron job进行日志的切割备份。
附上使用FileAppender的配置
log4j.rootLogger=INFO, file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.Encoding=UTF-8
log4j.appender.file.File=${catalina.home}/logs/web.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] (%c{1}:%L) - %m%n
简书:log4j的实现原理与思考