我想要一个由管理数据的多个任务生成的诊断日志。这些任务可能在多个线程中。每个任务都需要将一个元素(可能带有子元素)写入日志;进去快点出去。如果这是一个单任务情况,我会使用XMLStreamWriter,因为它似乎是简单/功能的最佳匹配,而不必在内存中保存一个膨胀的XML文档。
但这不是一个单任务的情况,我不确定如何最好地确保这是“线程安全”,其中“线程安全”在这个应用程序中意味着每个日志元素应该正确和连续地写入日志(一个接一个,不以任何方式交错)。
有什么建议吗?我有一个模糊的直觉,即要使用的方法是使用一个日志元素队列(每个元素都可以快速生成:我的应用程序忙于执行对性能敏感的实际工作),并且有一个单独的线程来处理日志元素并将它们发送到文件,以便日志记录不会中断生成器。
日志记录不一定是XML,但我确实希望它具有结构化和机器可读性。
编辑:我在引号中加上“threadsafe”。 Log4j似乎是一个显而易见的选择(对我来说很新但对社区来说很老),为什么重新发明轮子......
答案 0 :(得分:22)
我认为你走错了路。你说“线程安全”,但实际上你的意思是“序列化”。 Threadsafe意味着一个线程不会干扰来自其他线程的数据。大多数情况下,线程问题事先得到解决,你不应该只为了记录而担心它。例如,如果你写:
myVariableSum = 0 + myVariable;
//here comes other thread - Not very likely!
logger.info("Log some INFO; myVariable has value" + myVariable.toString());
在执行计算(第一行)但调用日志记录方法之前,您必须确保myVariable尚未被其他某个线程更改。如果发生这种情况,您将记录未用于执行操作的脏值,而是记录由其他某个线程分配的值。这通常是照顾;例如,本地(方法级别)变量不能被其他线程更改。无论如何,如果您在登录时不得不担心这一点,那么99%的程序已经存在严重的线程问题。
所有主要的日志记录框架本身都是“线程安全的”,这意味着它们可以部署在多线程环境中,并且不会在内部显示与上述类似的问题。
使跟踪按顺序出现在日志中实际上通常称为调用的“序列化”。序列化日志写入将是任何多线程应用程序的主要性能瓶颈。如果您使用日志框架(如log4j),则所有线程的跟踪将在单个位置或多或少地出现,以便它们发生。但是,一列通常是线程名称,因此您可以通过线程轻松过滤日志数据;每个线程将按时间顺序记录其数据。看看这个链接:
http://logging.apache.org/log4j/1.2/faq.html#1.7
最后,如果序列化日志写入是您真正需要的,那么您可以使用某种结构,如java.util.concurrent.BlockingQueue来路由您的消息。
答案 1 :(得分:21)
使用日志框架,例如Log4j。
答案 2 :(得分:9)
使用logback-classic。它是log4j的更新更好的实现。
答案 3 :(得分:5)
我倾向于在Log4J之上使用SLF4J。如果您要在生产环境中关闭很多日志记录语句,parameterized logging功能特别有吸引力。
它也可以运行在java.util.logging的顶部或使用它自己的简单输出。
答案 4 :(得分:4)
您可以使用同步机制(如监视器或信号器)来确保在接受下一个请求之前处理一个日志请求。这可以从调用日志记录例程的代码中隐藏。
答案 5 :(得分:4)
使用日志框架,例如Log4。
如果您对输出不满意,可以编写自己的Appender,Filter,无论如何调整它只是写。所以你甚至可以做一些缓存来重新安排条目,虽然我并不是说这是一个好主意。
答案 6 :(得分:4)
使用实现某种形式的the NDC pattern的日志框架,例如Log4J。
答案 7 :(得分:3)
log4j已经成为多年来java日志记录的标准。但是如果你不喜欢外部依赖,那么java.util.logging包提供了一个可接受的解决方案。
答案 8 :(得分:2)
我只对特殊日志有类似的问题和实施要求。我的解决方案是:
我的blockinglinkedqueue
大小为应用流量/分钟*2
的大小。
所有线程都将对象放入队列并完成作业。
将头对象从队列中分离出来的Log-Writer
线程,并使用单独的appender将其写入log4j
文件。这个appender没有用于系统日志。
这可确保日志按顺序写入并始终按顺序排列。
这不会影响应用程序的性能,因为日志写入是一个完全独立的过程,不会造成瓶颈。
您还可以使用aysncappender
的{{1}}。
答案 9 :(得分:1)
如果必须,您可以使用单写入器/单读取器FIFO或队列自行滚动。
答案 10 :(得分:0)
答案 11 :(得分:0)
这是一个老问题,但这是我以编程方式使用Log4J的解决方案。
LogFactory类
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import java.util.Properties;
public class LogFactory {
private final static ThreadLocal<Logger> logFactory = new ThreadLocal<>();
public static void createNewLogger(String className) {
Logger log = Logger.getLogger("Thread" + className);
Properties props = new Properties();
props.setProperty("log4j.appender.file", "org.apache.log4j.RollingFileAppender");
props.setProperty("log4j.appender.file.maxFileSize", "100MB");
props.setProperty("log4j.appender.file.Append", "false");
props.setProperty("log4j.", "100MB");
props.setProperty("log4j.appender.file.maxBackupIndex", "100");
props.setProperty("log4j.appender.file.File", "logs/" + className + ".log");
props.setProperty("log4j.appender.file.threshold", "info");
props.setProperty("log4j.appender.file.layout", "org.apache.log4j.PatternLayout");
props.setProperty("log4j.appender.file.layout.ConversionPattern", "%d{yyyy-MM-dd HH-mm-ss} | %-5p | %C{1}:%L | %m%n");
props.setProperty("log4j.appender.stdout", "org.apache.log4j.ConsoleAppender");
props.setProperty("log4j.appender.stdout.Target", "System.out");
props.setProperty("log4j.logger." + "Thread" + className, "INFO, file");
PropertyConfigurator.configure(props);
logFactory.set(log);
}
public static Logger getLogger() {
return logFactory.get();
}
}
然后使用以下方法初始化记录器
logFactory.createNewLogger(String.valueOf(Thread.currentThread().getId()));
logFactory.getLogger().info(" TEST . Thread id is: " + id);