简体   繁体   English

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

[英]Best practices for Java logging from multiple threads?

I want to have a diagnostic log that is produced by several tasks managing data. 我想要一个由管理数据的几个任务生成的诊断日志。 These tasks may be in multiple threads. 这些任务可能在多个线程中。 Each task needs to write an element (possibly with subelements) to the log; 每个任务都需要将一个元素(可能带有子元素)写入日志; get in and get out quickly. 进去快点出去。 If this were a single-task situation I'd use XMLStreamWriter as it seems like the best match for simplicity/functionality without having to hold a ballooning XML document in memory. 如果这是一个单任务的情况,我会使用XMLStreamWriter,因为它似乎是简单/功能的最佳匹配,而不必在内存中保存一个膨胀的XML文档。

But it's not a single-task situation, and I'm not sure how to best make sure this is "threadsafe", where "threadsafe" in this application means that each log element should be written to the log correctly and serially (one after the other and not interleaved in any way). 但这不是一个单任务的情况,我不确定如何最好地确保这是“线程安全”,其中“线程安全”在这个应用程序中意味着每个日志元素应该正确和连续地写入日志(一个后另一个,不以任何方式交错)。

Any suggestions? 有什么建议? I have a vague intuition that the way to go is to use a queue of log elements (with each one able to be produced quickly: my application is busy doing real work that's performance-sensitive), and have a separate thread which handles the log elements and sends them to a file so the logging doesn't interrupt the producers. 我有一个模糊的直觉,即要使用的方法是使用一个日志元素队列(每个元素都可以快速生成:我的应用程序忙于执行对性能敏感的实际工作),并且有一个单独的线程来处理日志元素并将它们发送到文件,以便日志记录不会中断生产者。

The logging doesn't necessarily have to be XML, but I do want it to be structured and machine-readable. 日志记录不一定必须是XML,但我确实希望它是结构化的和机器可读的。

edit: I put "threadsafe" in quotes. 编辑:我把“threadsafe”放在引号中。 Log4j seems to be the obvious choice (new to me but old to the community), why reinvent the wheel... Log4j似乎是一个显而易见的选择(对我来说很新但对社区来说很老),为什么重新发明轮子......

I think you are on the wrong path. 我想你走错了路。 You say “threadsafe” but you actually mean “serialized”. 你说“线程安全”,但实际上你的意思是“序列化”。 Threadsafe means that one thread will not interfere with data from other thread. Threadsafe意味着一个线程不会干扰来自其他线程的数据。 Most of the time, threading issues are resolved beforehand and you should not worry about it just for logging sake. 大多数情况下,线程问题事先得到解决,你不应该只为了记录而担心它。 For example, if your write: 例如,如果你写:

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

You have to make sure that myVariable has not been changed by some other thread from the moment calculation (first line) was performed but before logging method was called. 您必须确保在执行计算(第一行)时但在调用日志记录方法之前,某些其他线程未更改myVariable。 If this happens, you will log dirty value that was not used to perform the operation but value that was assigned by some other thread. 如果发生这种情况,您将记录未用于执行操作的脏值,而是记录由其他某个线程分配的值。 This is generally taken care of; 这通常是照顾; for example local (method level) variable can not be changed by other thread. 例如,本地(方法级别)变量不能被其他线程更改。 Anyway, if you have to worry about this when logging, than 99% that your program has serious threading issues already. 无论如何,如果您在登录时不得不担心这一点,那么99%的程序已经存在严重的线程问题。
All major logging frameworks are by themselves “threadsafe” meaning they can be deployed in multithreaded environments and will not display problems similar to one described above internally. 所有主要的日志记录框架本身都是“线程安全的”,这意味着它们可以部署在多线程环境中,并且不会在内部显示与上述类似的问题。
Getting traces to appear in log in order they happen is actually usually called “serialization” of calls. 使跟踪按顺序出现在日志中实际上通常称为调用的“序列化”。 Serializing log writes will be a major performance bottleneck on any multithreaded app. 序列化日志写入将是任何多线程应用程序的主要性能瓶颈。 If you use logging framework, like log4j, traces from all threads will appear in single place more or less in order they happen. 如果您使用日志框架(如log4j),则所有线程的跟踪将在单个位置或多或少地出现,以便它们发生。 However, one column is generally Thread name, so you can easily filter your log data by thread; 但是,一列通常是线程名称,因此您可以通过线程轻松过滤日志数据; each thread will log its data in chronological order. 每个线程将按时间顺序记录其数据。 Check out this link: http://logging.apache.org/log4j/1.2/faq.html#1.7 看看这个链接: http//logging.apache.org/log4j/1.2/faq.html#1.7
Finally, if serializing log writes is what you really need, then you could use some kind of structure, like java.util.concurrent.BlockingQueue to route your messages. 最后,如果序列化日志写入是您真正需要的,那么您可以使用某种结构,如java.util.concurrent.BlockingQueue来路由您的消息。

使用日志框架,例如Log4j

Use logback-classic. 使用logback-classic。 It is a newer and better implementation of log4j. 它是log4j的更新更好的实现。

You could use synchronization mechanisms (like a monitor or a semaphor) to make sure, that one log request is processed before accepting the next. 您可以使用同步机制(如监视器或信号器)来确保在接受下一个请求之前处理一个日志请求。 This could all be hidden from the code calling the logging routines. 这可以从调用日志记录例程的代码中隐藏。

I tend to use SLF4J on top of Log4J. 我倾向于在Log4J之上使用SLF4J The parameterized logging functionality is especially attractive if you are going to have a lot of logging statements that may well get switched off in a production environment. 如果您要在生产环境中关闭很多日志记录语句,那么参数化日志记录功能尤其具有吸引力。

It can also run over the top of java.util.logging or use it's own simple output. 它也可以运行在java.util.logging的顶部或使用它自己的简单输出。

Use a logging framework, such as Log4. 使用日志框架,例如Log4。

and if you are not happy with the output you can write your own Appender, Filter, whatever to tweak it just write. 如果您对输出不满意,可以编写自己的Appender,Filter,无论如何调整它只是写。 So you could do even some caching to rearrange the entries, although I am not saying this is a good idea. 所以你甚至可以做一些缓存来重新安排条目,虽然我不是说这是一个好主意。

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

log4j is and has been the standard for java logging for many years. log4j已经成为多年来java日志记录的标准。 But if you don't fancy an external dependency then the java.util.logging package provides an acceptable solution. 但是如果你不喜欢外部依赖,那么java.util.logging包提供了一个可接受的解决方案。

I had a similar problem and implementation demands for special logs only. 我只对特殊日志有类似的问题和实现要求。 My solution was: 我的解决方案是:

  1. I took a blockinglinkedqueue with size of *2 of the app's traffic/min. 我使用了应用流量/分钟大小为*2blockinglinkedqueue

  2. All threads put the object in the queue and finishes the job. 所有线程都将对象放入队列并完成作业。

  3. Separate Log-Writer thread taking head object from queue and writing it to log4j file using a separate appender. 单独的Log-Writer线程将头对象从队列中取出,并使用单独的appender将其写入log4j文件。 This appender was not used for systemlogs. 这个appender没有用于系统日志。

This ensures that logs are written serially and always are in order. 这可确保日志按顺序写入并始终按顺序排列。

This will not affect performance of the application since log writing is a completely separate process and will not create a bottleneck. 这不会影响应用程序的性能,因为日志写入是一个完全独立的过程,不会产生瓶颈。

You can also use aysncappender of log4j . 您也可以使用log4j aysncappender

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

This is an old question but here's my solution using Log4J programmatically. 这是一个老问题,但这是我的编程方式使用Log4J的解决方案。

LogFactory class 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();
    }

}

Then to initialise the the logger use the following approach 然后初始化记录器使用以下方法

logFactory.createNewLogger(String.valueOf(Thread.currentThread().getId()));
logFactory.getLogger().info(" TEST . Thread id is: " + id);

Developing this yourself in a thread-safe way is not trivial, so you should really use an existing logging framework that is thread-safe. 以线程安全的方式自己开发这个并不简单,所以你应该真正使用一个线程安全的现有日志框架。 The most commonly used one is Log4J , which is thread-safe (see the FAQ ). 最常用的是Log4J ,它是线程安全的(参见FAQ )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM