目录

logrotate问题及分析

目录

在这之前对logrotate理解存在误区,以为只要对目标文件配置logrotate,就可以搞定日志轮转。

生产环境下,偶有发生mongod日志为空的问题,即日志轮转后,新的mongod日志并没有写到原始日志文件,日志文件长度始终是0,此时执行mongodb自身的logRotate命令也无济于事,除非重启mongod,如果碰巧这个时间点数据库出了故障,日志都没有,搞毛线啊~~

所以花时间了解下logrotate,项目托管在github.com/logrotate/logrotate

由于我的logrotate配置包含copytruncate选项,即先拷贝再清空,直接看关键代码,函数调用关系是rotateLogSet() => rotateSingLog() => copyTruncate()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 拷贝日志文件副本
if (sparse_copy(fdcurr, fdsave, sb, saveLog, currLog) != 1) { 
    close(fdcurr);
    close(fdsave);
    message(MESS_ERROR, "error copying %s to %s: %s\n", currLog,
            saveLog, strerror(errno));
    unlink(saveLog);
    return 1;
}

// 清空原始日志文件
if (flags & LOG_FLAG_COPYTRUNCATE) {
    message(MESS_DEBUG, "truncating %s\n", currLog);

    if (!debug) {
        fsync(fdsave);
        if (ftruncate(fdcurr, 0)) {
        message(MESS_ERROR, "error truncating %s: %s\n", currLog,
            strerror(errno));
        close(fdcurr);
        close(fdsave);
        return 1;
        }
    }
} else
    message(MESS_DEBUG, "Not truncating %s\n", currLog);

这里逻辑很简单,直接调用 ftruncate() 清空文件,回到文章开头的问题,mongod进程在写日志,logrotate进程执行 ftruncate(),至少logrotate没有对文件加锁,我觉得是两个进程同时写日志导致问题发生。

所以,mongodb日志轮转正确的方法是先执行mongodb自带的logRotate命令,然后利用Linux logrotate工具处理转储日志文件。

在此过程中,学习掌握了文件截断函数 truncate()ftruncate()

还有,truncate() 重置了文件长度,如果其他进程也正在写此文件,文件偏移是怎么处理的?答案是O_APPEND,该选项使得每一次写操作,先将定位到文件末尾,以保证追加写入。


参考资料