缓冲日志记录器(Java)

6

我正在考虑构建一个日志系统,该系统将日志语句推送到内部缓冲区,直到达到预定义的容量,然后一次性转储(刷新)整个缓冲区。

这是因为我喜欢在我的方法中撒播大量的TRACE语句(这样我就可以在每几行上看到发生了什么;至少对我来说更容易调试)。而且我担心,由于可能会有数百/数千个日志语句在各处触发,这样的大量I/O需求会拖慢我的程序。

“缓冲”记录器解决方案可能会缓解这种情况。

三个问题:

  1. 类似于这样的东西已经存在吗?不愿意重复造轮子,但在线搜索没有找到任何信息。
  2. 我想到了这样一个事实,即当程序意外停止(运行时异常等)并且记录器没有被刷新时,我很可能会失去日志语句。在这种情况下,我希望记录器覆盖finalize(),以便如果程序在其缓冲区中仍有项目的情况下终止,它可以在退出之前将它们刷新(发布)。有什么想法吗?
  3. 这是一个可怕的想法吗?如果是,为什么!

2
大多数文件系统已经处理了缓冲,我怀疑(未经确认)基于文件的记录器已经使用了缓冲写入器。如果程序关闭,可能会调用finalize(),但也可能不会。这不是一个坏主意,只是我不确定它是否对仅在开发期间发生的事情有良好的投资回报率。 - Dave Newton
感谢您的见解,戴夫。问题是:我认为这将是任何环境(包括生产环境)中的好工具。这样我就可以检查生产日志以快速重现任何缺陷等。您能分享一下您认为为什么只适用于开发阶段吗?(我很好奇!)再次感谢。 - IAmYourFaja
因为跟踪和调试日志级别只是用于跟踪和调试,而不是生产。如果您的系统需要那么多信息来追踪问题,可能有其他问题。如果您真的认为需要那么多信息,可以考虑使用JMS记录器。您是否已经证明记录实际上是一个问题? - Dave Newton
不是 :-) 只是我的“蜘蛛感觉”。谢谢你的快速教训! - IAmYourFaja
6个回答

5

如果可能的话,请不要重新发明这个轮子。看看Log4j或更好的slf4j

如果您不跟踪,则log4j和slf4j都非常高效,因此在生产系统中,您可以降低日志记录级别并仍然具有良好的性能。

log4j和slf4j都会立即写入日志文件并刷新,不会默认缓冲,原因是您希望在日志文件中看到导致崩溃的异常。如果您真的想添加缓冲,可以这样做(FileAppender#bufferedIO

就finalize()而言,不能保证在退出时调用它。来自System#runFinalizersOnExit

已弃用。此方法本质上是不安全的。在其他线程同时操作这些对象时,可能会导致终结器在活动对象上被调用,从而导致不稳定的行为或死锁。启用或禁用退出时终结;这样做指定了在 Java 运行时退出之前将运行所有具有尚未自动调用的终结器的终结器的对象的终结器。默认情况下,禁用退出时终结

我的强调。所以,似乎缓冲日志记录器会存在固有问题。


2
我会证明你提出的第三个问题。这是一个可怕的想法,与你作为程序员的技能无关。它所涉及的是像log4j这样的项目暴露的场景数量以及它们考虑到的不同边缘情况。我认为这个任务对你来说相当大,并且将在日后引起很多头痛。

是的,我开始明白了。感谢你的坦率! - IAmYourFaja

1

Log4J已经支持IO缓冲。有关Log4J的一些不错的性能提示在这里


1
链接已经失效,请尝试复制/粘贴相关内容,这样即使网站宕机也能保留信息。如果信息太大,则可以提供链接并希望其保持在线状态:D - Adrian B.

0

不知道是否存在现有的框架,据我所知没有一个在本地内存中“缓冲”。但是可以查看标准的Log4J等。

finalize() 不是正确的方法。使用 Runtime.addShutdownHook()。最近学到的内容请参见这里


0

我们在生产服务器上也做了类似的事情,只不过它们是通过网络连接发送日志消息的。我们只有一个有界的链式队列,并根据队列是否已满来决定弹出调试消息并保留错误消息。然后我们有一个后台线程将消息发送到服务器。

  1. 我不知道log4j或slf4j中是否有这样的功能。

  2. 您丢失消息的唯一方式是JVM崩溃,在这种情况下,缓冲区编写器将遇到相同的问题。如果您的编写线程不是守护程序,则可以在应用程序关闭之前完成写入日志消息。

  3. 唯一的原因是,如果您的日志记录没有性能问题,那么这将是一个可怕的想法。最好确保将日志记录调用隔离到自己的日志记录包装器中,以备将来需要将其排队。但是短期内,只需直接写入log4j或其他内容即可。您还可以在此包装类中放置一些统计信息,以查看日志记录消息是否完全阻塞了进程,然后做出明智的决策,通过排队来提高其性能。


0

如前所述 - 不要重复造轮子 - 使用log4j或更好的logback(它的后继者),但要明智地使用。默认的简单附加器会立即写入和刷新磁盘,这将影响您的应用程序的整体性能。通常,附加器在调用者的线程上执行繁重的工作。为了避免这种情况,您可以编写自己的附加器,以异步方式执行此操作(带有缓冲区),或者使用包装器 - 根据框架,您可以使用一些附加器包装器来实现异步行为,而无需编写任何代码。我有一个这样的附加器here(请参见页面底部),您可以自由使用代码或仅获取想法。我的附加器针对TCP套接字工作,但问题是相同的 - 防止调用者阻塞并在单独的线程上执行刷新,以便顶部的应用程序永远不会感受到它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接