从多个线程进行Java日志记录的最佳实践?

我想要一个由管理数据的几个任务生成的诊断日志。 这些任务可能在多个线程中。 每个任务都需要将一个元素(可能带有子元素)写入日志; 进去快点出去。 如果这是一个单任务的情况,我会使用XMLStreamWriter,因为它似乎是简单/function的最佳匹配,而不必在内存中保存一个膨胀的XML文档。

但这不是一个单任务的情况,我不确定如何最好地确保这是“线程安全”,其中“线程安全”在这个应用程序中意味着每个日志元素应该正确和连续地写入日志(一个后另一个,不以任何方式交错)。

有什么建议么? 我有一个模糊的直觉,要走的路是使用一个日志元素队列(每个元素都可以快速生成:我的应用程序正在忙于执行对性能敏感的实际工作),并且有一个单独的线程来处理日志元素并将它们发送到文件,以便日志记录不会中断生成器。

日志记录不一定必须是XML,但我确实希望它是结构化的和机器可读的。

编辑:我把“threadsafe”放在引号中。 Log4j似乎是一个显而易见的选择(对我来说很新但对社区来说很老),为什么重新发明轮子……

使用日志框架,例如Log4j 。

我认为你走错了路。 你说“线程安全”,但实际上你的意思是“序列化”。 Threadsafe意味着一个线程不会干扰来自其他线程的数据。 大多数情况下,线程问题事先得到解决,你不应该只为了记录而担心它。 例如,如果你写:

myVariableSum = 0 + myVariable; //here comes other thread - Not very likely! logger.info("Log some INFO; myVariable has value" + myVariable.toString()); 

您必须确保在执行计算(第一行)时但在调用日志记录方法之前,某些其他线程未更改myVariable。 如果发生这种情况,您将记录未用于执行操作的脏值,而是记录由其他某个线程分配的值。 这通常是照顾; 例如,其他线程无法更改本地(方法级别)变量。 无论如何,如果您在登录时不得不担心这一点,那么99%的程序已经存在严重的线程问题。
所有主要的日志记录框架本身都是“线程安全的”,这意味着它们可以部署在multithreading环境中,并且不会在内部显示与上述类似的问题。
使跟踪按顺序出现在日志中实际上通常称为“序列化”调用。 序列化日志写入将是任何multithreading应用程序的主要性能瓶颈。 如果您使用日志框架(如log4j),则所有线程的跟踪将在单个位置或多或少地出现,以便它们发生。 但是,一列通常是线程名称,因此您可以通过线程轻松过滤日志数据; 每个线程将按时间顺序记录其数据。 看看这个链接: http : //logging.apache.org/log4j/1.2/faq.html#1.7
最后,如果序列化日志写入是您真正需要的,那么您可以使用某种结构,如java.util.concurrent.BlockingQueue来路由您的消息。

使用logback-classic。 它是log4j的更新更好的实现。

我倾向于在Log4J之上使用SLF4J 。 如果您要在生产环境中关闭很多日志记录语句,那么参数化日志记录function尤其具有吸引力。

它也可以运行在java.util.logging的顶部或使用它自己的简单输出。

使用日志框架,例如Log4。

如果你对输出不满意,你可以编写自己的Appender,Filter,无论如何调整它只是写。 所以你甚至可以做一些缓存来重新安排条目,虽然我不是说这是一个好主意。

使用实现某种forms的NDC模式的日志框架,如Log4J 。

您可以使用同步机制(如监视器或信号器)来确保在接受下一个日志请求之前处理一个日志请求。 这可以从调用日志记录例程的代码中隐藏。

log4j已经成为多年来java日志记录的标准。 但是如果你不喜欢外部依赖,那么java.util.logging包提供了一个可接受的解决方案。

我只对特殊日志有类似的问题和实现要求。 我的解决方案是:

  1. 我使用了应用程序流量/分钟大小为*2blockinglinkedqueue

  2. 所有线程都将对象放入队列并完成作业。

  3. 单独的Log-Writer线程从队列中获取头对象并使用单独的appender将其写入log4j文件。 这个appender没有用于系统日志。

这可确保日志按顺序写入并始终按顺序排列。

这不会影响应用程序的性能,因为日志写入是一个完全独立的过程,不会产生瓶颈。

您也可以使用log4j aysncappender

以线程安全的方式自己开发这个并不简单,所以你应该真正使用一个线程安全的现有日志框架。 最常用的是Log4J ,它是线程安全的(参见FAQ )。

如果必须,您可以使用单写入器/单读取器FIFO或队列自行滚动。