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

问题描述 投票:34回答:13

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

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

logging
13个回答
20
投票

如果您经常进行读/写操作,那么通过单次打开/关闭来保持文件打开的效率会更高效。

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

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

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


0
投票

每次关闭文件的好处是操作系统将保证新消息写入磁盘。如果您打开文件并且程序崩溃,则可能无法写入整个文件。您也可以通过执行fflush()或您正在使用的语言中的等效内容来完成相同的操作。


0
投票

作为您的应用程序的用户,我更喜欢它不打开文件,除非它是应用程序的真正要求。在系统崩溃等情况下,还有一件事可能出错。


0
投票

我会在每次写入(或批量写入)时打开和关闭。如果这样做会导致桌面应用程序出现性能问题,那么您可能会过于频繁地写入日志文件(尽管我确信可能存在大量写入的合法原因)。


0
投票

对于大型密集型应用程序,我通常做的是在应用程序的持续时间内保持日志文件处于打开状态,并且有一个单独的线程可以定期将内存中的日志内容刷新到HDD。文件打开和关闭操作需要系统调用,如果你查看较低级别,这是很多工作。


16
投票

通常,正如其他人所说的那样,保持文件的性能开放(打开是一个相对较慢的操作)。但是,如果您保持文件打开并且人们要么删除日志文件或截断日志文件,您需要考虑会发生什么。这取决于开放时使用的标志。 (我正在解决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(和更改)。反社会,但可能。


4
投票

为了表现,请保持开放。为安全起见,经常冲洗。

这意味着运行时库不会尝试缓冲写入,直到它有大量数据 - 您可能会在写入之前崩溃!


3
投票

我倾向于让它们保持打开状态 - 但是打开它们并设置文件共享权限以允许其他读者,并确保用每条消息刷新日志输出。

我讨厌的程序甚至不会让你在日志文件运行时查看它们,或者日志文件没有刷新并且落后于正在发生的事情。


2
投票

保持开放通常更好。

如果您担心能够从其他进程读取它们,则需要确保用于打开/创建它们的共享模式允许其他人读取它们(但显然不能写入它们)。

如果您担心在发生崩溃时丢失数据,则应定期刷新/提交缓冲区。


2
投票

这是一个权衡。每次打开和关闭文件使得更有可能在程序崩溃时在磁盘上更新文件。另一方面,打开文件,寻找到最后,并将数据附加到其中涉及一些开销。

在Windows上,您无法在文件打开时移动/重命名/删除该文件,因此打开/写入/关闭可能对长时间运行的进程有帮助,您可能偶尔需要将旧日志内容存档而不会中断作家。

在我完成这种日志记录的大多数情况下,我保持文件打开,并使用fflush()使文件更新,如果程序崩溃了。


1
投票

打开和关闭。在系统崩溃的情况下,可以将您从损坏的文件中保存。


1
投票

我认为没有理由关闭它。

另一方面,关闭和重新打开需要一些额外的时间。


1
投票

我可以想到一些你不想保持文件打开的原因:

  • 如果在几个不同的应用程序,用户或应用程序实例之间共享日志文件,则可能存在锁定问题。
  • 如果您没有正确清除流缓冲区,那么当该应用程序崩溃并且您最需要它们时,可能会丢失最后几个条目。

另一方面,打开文件可能很慢,即使在追加模式下也是如此。最后,它归结为您的应用正在做什么。

© www.soinside.com 2019 - 2024. All rights reserved.