检测该日志文件已被删除或在POSIX系统上被截断?

假设一个长时间运行的进程写入一个日志文件。 假设日志文件无限期地保持打开状态。 假设一个粗心的系统pipe理员删除那个日志文件。 程序能检测到这种情况发生了吗?

假设fstat()会报告已删除文件的链接计数为零是否安全?

截断,在我看来,有点棘手。 部分取决于文件描述符是否以O_APPEND模式运行。 如果日志文件没有用O_APPEND运行,那么程序日志描述符的当前写入位置不会改变,截断将删除前导字节,但是程序继续在“结束”处写入,留下幻影间隙零字节(它们读为零,但不一定占用磁盘空间)。

如果程序使用O_APPEND运行,那么它将在当前存在的文件末尾写入。 观察截断的唯一方法是注意文件位置不在程序期望的位置 – 这又意味着明确地跟踪该位置。

总的来说,我并不担心截断和删除,但任何想法都会受到欢迎。

如果文件被硬连接或重命名,检查fstat()返回链接计数为零将失败。 我可能会定期比较stat()的inode号码和fstat()的号码。

我不确定截断。

tail -F检查删除和可能截断,所以我会检查它的来源,看看它是如何实现它。

假设这个粗心的系统管理员杀死了这个进程。 你真的想防止管理员做随机的事情吗? 我想你只是寻找一种方式来不时开始一个新的日志文件,如使用logrotate 。 在那里提供一种方法来手动让程序重新打开日志文件就足够了。 标准的方法是在程序中监听HUP-Signal,如果到达,重新打开日志文件:

 #include <signal.h> volatile int f_sighup; void sighup_handler() { f_sighup = 1; } void trap_sighup() { struct sigaction sa; int rv; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = &sighup_handler; rv = sigaction(SIGHUP, &sa, NULL); if (-1 == rv) { fprintf(stderr, "warning: setting SIGHUP signal handler failed"); } } int main() { f_sighup = 0; trap_sighup(); ... } 

然后定期检查f_sighup中的f_sighup标志,看是否应该重新打开日志文件。 这对于logrotate这样的工具来说很好,它可以重命名旧的日志文件,然后调用kill -s HUP $PID 。 粗心的系统管理员可以在删除(或者更好的重命名)旧的日志文件之后手动执行此操作。

你可以使用inotify来监视你的日志文件,监视文件系统事件。

回答søren-holm的回答

当文件关闭时,修改时间被改变。

这似乎不正确:

 import os from time import sleep TMPF = '/tmp/f' def print_stats(): print("%s, %s" % (os.stat(TMPF).st_mtime, os.stat(TMPF).st_ctime)) sleep(1.1) print("Opening...") with open(TMPF, 'w') as f: print_stats() print("Writing...") os.write(f.fileno(), 'apple') print_stats() print("Flushing...") f.flush() print_stats() print("Closing...") print_stats() 

生产:

 Opening... 1483052647.08, 1483052647.08 Writing... 1483052648.18, 1483052648.18 Flushing... 1483052648.18, 1483052648.18 Closing... 1483052648.18, 1483052648.18 

无可否认,这里有一些Python魔法, write()不能合理保证被自动刷新,但是这个观点表明mtime在文件被修改时被更新,而不是在文件被关闭的时候被更新。 ctime的行为将取决于您的文件系统及其挂载选项。

当文件关闭时,修改时间被改变。 所以定期检查使用stat()的mtime会工作。