当前位置:  开发笔记 > 运维 > 正文

是应该在每次写入时打开/关闭日志文件流还是在桌面应用程序的生命周期内保持打开状态?

如何解决《是应该在每次写入时打开/关闭日志文件流还是在桌面应用程序的生命周期内保持打开状态?》经验,为你挑选了2个好方法。

日志类是应该在每次写入日志文件时打开/关闭日志文件流,还是应该在整个应用程序的生命周期内保持日志文件流打开,直到所有日志记录完成为止?

我在桌面应用程序的上下文中询问.我见过人们都是这样做的,并且想知道哪种方法能够为记录器带来最好的全能结果.



1> Brian R. Bon..:

如果您经常进行读/写操作,则通过单次打开/关闭可以使文件在生命周期内保持打开状态更有效.

您可能希望定期刷新或在每次写入后刷新,以防您的应用程序崩溃,您可能没有将所有数据写入您的文件.在基于Unix的系统上使用fflush,在Windows上使用FlushFileBuffers.

如果您也在Windows上运行,则可以使用带有FILE_FLAG_NO_BUFFERING的CreateFile API在每次写入时直接转到文件.

这也是更好地保持该文件打开的寿命,因为每次打开/关闭时间你可能有失败,如果该文件正在使用中.例如,您可能有一个备份应用程序,可以在备份时运行并打开/关闭您的文件.这可能会导致您的程序无法访问您自己的文件.理想情况下,您希望始终打开文件并在Windows上指定共享标志(FILE_SHARE_READ).在基于Unix的系统上,共享将是默认的.



2> Jonathan Lef..:

通常,正如其他人所说的那样,保持文件的性能开放(打开是一个相对较慢的操作).但是,如果您保持文件打开并且人们要么删除日志文件或截断日志文件,您需要考虑会发生什么.这取决于开放时使用的标志.(我正在解决Unix问题 - 类似的考虑因素可能适用于Windows,但我会接受那些比我更了解情况的修正).

如果有人看到日志文件增长到1 MiB然后将其删除,那么应用程序将更加明智,Unix将保持日志数据安全,直到应用程序关闭日志.更糟糕的是,用户会感到困惑,因为他们可能创建了一个与旧版本同名的新日志文件,并对应用程序"停止记录"的原因感到困惑.当然,它没有; 它只是记录到其他人无法获得的旧文件.

如果有人注意到日志文件已经增长到1 MiB然后截断它,那么应用程序也将更加明智.但是,根据日志文件的打开方式,您可能会得到奇怪的结果.如果文件没有用O_APPEND(POSIX-speak)打开,那么程序将继续在日志文件中以其当前偏移量写入,并且文件的前1 MiB将显示为零字节流 - 这是apt混淆查看文件的程序.

如何避免这些问题?

使用O_APPEND打开日志文件.

定期使用fstat()文件描述符并检查是否st_nlink为零.

如果链接计数变为零,则有人删除了您的日志文件.是时候关闭它了,重新打开一个新的.与stat()或相比open(),fstat()应该是快速的; 它基本上是从已经在内存中的东西中复制信息,不需要进行名称查找.所以,你每次打算写作时都应该这样做.

建议:

确保有一种机制告诉程序切换日志.

确保在消息中记录完整的日期和时间.

我遇到了一个提出时间而不是日期的应用程序.今天早些时候,我有一个消息文件,从8月17日开始有一些条目(其中一条消息意外地包含了消息中的日期),然后是今天的一些条目,但我只能告诉它,因为我创建了它们.如果我在几周内查看日志文件,我无法确定它们创建的那一天(虽然我知道它们创建的时间).那种事很烦人.

您还可以查看Apache所执行的系统 - 它们具有处理日志文件的机制,还有处理日志轮换的工具.注意:如果应用程序确实保持单个文件打开,不使用追加模式,并且不计划日志轮换或大小限制,那么关于日志文件增长或者在开始时拥有零点的情况下你无能为力 - 其他而不是定期重新启动应用程序.

您应该确保尽快完成对日志的所有写入操作.如果使用文件描述符,那么只有内核缓冲; 这可能是可以接受的,但请考虑O_SYNCO_DSYNC选择open().如果使用文件流I/O,请确保后面跟着每个写入fflush().如果您有多线程应用程序,请确保每个应用程序都write()包含完整的消息; 不要试图单独写一部分消息.对于文件流I/O,您可能需要使用flockfile()和亲属将操作组合在一起.使用文件描述符I/O,您可以使用dprintf()格式化I/O到文件描述符(虽然不是绝对清楚,只dprintf()进行一次调用write()),或者也许writev() 在单个操作中写入单独的数据段.

顺便提一下,"包含"零的磁盘块实际上并未在磁盘上分配.你可以通过创建几个GiB的文件来搞砸人们的备份策略,但除了最后一个磁盘块之外的所有文件都只包含零.基本上(为了简洁而省略了错误检查和文件名生成):

int fd = open("/some/file", O_WRITE|O_CREATE|O_TRUNC, 0444);
lseek(fd, 1024L * 1024L * 1024L, 0);
write(fd, "hi", 2);
close(fd);

这占用磁盘上的一个磁盘块 - 但是(未压缩)备份时为1 GiB(和更改),还原时为1 GB(和更改).反社会,但可能.

推荐阅读
mobiledu2402851173
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有