如何在Java中将多个线程和类写入同一个日志文件?

时间:2014-01-07 15:23:36

标签: java multithreading file singleton java.util.logging

在java应用程序中,我有几个Classes和Threads。我希望他们都将他们的日志写入一个文件。我怎样才能做到这一点?

此外还有一些问题:

  1. 如果我为它们定义相同的名称,即使在不同的类中,我也会收到相同的记录器实例。需要说明的是,以下两个记录器是相同的:

    class MyClass1 {
        Logger logger1 = Logger.getLogger("theSameName");
    }
    class MyClass2{
        Logger logger2 = Logger.getLogger("theSameName");
    }
    
  2. 如果我在两个具有相同文件名的独立线程中定义两个FileHandler,会发生什么? 他们两个都写到同一个文件? 如果是,如果第一个文件已经打开,第二个文件尝试打开并写入文件时会发生什么?

  3. 如果我尝试将多个FielHandler添加到记录器中,会发生什么情况,如下所示:

    logger.addHandler(myFileHandler); // in Thread one
    ...
    logger.addHandler(myFileHandler); // in Thread two
    
  4. 我是否会收到RuntimeException,因为这两个FileHandler可能会尝试同时打开同一个文件?

    我正在使用java-util-logging而不是log4j

5 个答案:

答案 0 :(得分:3)

创建一个singleton日志类,以便任何其他类或线程尝试创建它的副本,实际上将使用相同的实例。

使用java.util.logging的示例实现:

public class LogMe {        
    private static LogMe logMe;
    private static Logger logger;    
    private static FileHandler fh;
    private static Formatter sf;

    public LogMe() {
        //Make this class a singleton
        if (logMe != null) {
            return;
        }

        //Create the log file            
        try {
            fh = new FileHandler("../xyz/LogFile.log");
        } catch (Exception e) {
            e.printStackTrace();
        }

        sf = new SimpleFormatter();
        fh.setFormatter(sf);            
        logger.addHandler(fh);

        //Part of making this class a singleton
        logger = Logger.getLogger("LogMe");
        logMe = this;        
    }

    public Logger getLogger() {
        return LogMe.logger;
    }
}

然后在你的课程中你会像这样使用它:

class MyClass1 {
    LogMe logMe1 = new LogMe();
    Logger logger2 = logMe1.getLogger();
    logger.info("X 01");
}

class MyClass2 {
    LogMe logMe2 = new LogMe();
    Logger logger2 = logMe2.getLogger();
    logger.info("X 02");
}

请注意,无论您在类中命名LogMe及其Logger,它都不会产生任何影响,因为它们引用了同一个LogMe实例及其Logger }

日志文件中的示例输出如下所示:

Oct 1, 2015 10:43:47 AM 
INFO: X 01 
Oct 1, 2015 10:43:47 AM 
INFO: X 02

请注意,这些日志行的顺序取决于类的执行顺序。此外,您不需要任何同步,引用Logger的JavaDoc:

  

Logger上的所有方法都是多线程安全的。

答案 1 :(得分:2)

  

如果我为它们定义相同的名称,即使在不同的类中也会收到相同的记录器实例。

我不相信它是在任何地方定义的,但大多数人都喜欢它是同一个对象,它应该无关紧要。这些库不会根据Logger实例确定要写入哪个文件。

  

如果我在两个具有相同文件名的独立线程中定义两个FileHandler会发生什么?

最像您将获得两个日志处理程序。这意味着除非您尝试写入同一文件,否则所有内容都将被记录两次,在这种情况下它可能会被破坏。

没有充分理由这样做。只需在启动时定义配置一次。

  

如果我尝试将多个FielHandler添加到记录器中,会发生什么情况,如下所示:

底层库不太可能会注意到它会破坏文件。如果你真的需要知道,我建议你试试,但不要在生产中这样做。

  

我是否会收到RuntimeException,因为两个FileHandler可能会同时尝试打开同一个文件?

这不是FileOutputStream所做的或操作系统。你只是得到一个损坏的文件。

答案 2 :(得分:0)

创建一个将由您的应用程序使用的单例类。

以下是在Java中编写单例时需要了解的所有内容: http://learnjava.today/2015/08/the-best-singleton-in-java/

继续编码;)

答案 3 :(得分:0)

package javaloggers;

import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
//singleton class logger wrapper
public class LogWrap {
    public static final Logger aLogger = Logger.getLogger("myLogger");
    private static LogWrap instance = null;
    public static LogWrap getInstance(){
        if(instance==null){
            getLoggerReady();
            instance = new LogWrap();
        }
        return instance;
    }
    private static void getLoggerReady(){
        try{
            FileHandler fh = new FileHandler("test-log.txt");
            fh.setFormatter(new SimpleFormatter());
            aLogger.addHandler(fh);
            aLogger.setUseParentHandlers(false);
            aLogger.setLevel(Level.ALL);
        } catch(Exception e){
            System.out.print("Error: Logger creation issue: "+e);
        }           
    }
}
class test {
    public static void main(String args[]){
        //usage
        LogWrap logWrap = LogWrap.getInstance();
        logWrap.aLogger.severe( "Hello world!" );
    }
}

答案 4 :(得分:-2)

我有将数据推送到多个OutputStreams的代码。我用它将System.out复制到System.outFileOutputStream

我基本上会“重新”重新编码该代码,因此它使用多个输入流来管道传输到一个“主”文件。

这是我的代码

// This is an OutputStream that duplicate behavior across multiple OutStreams
public class MultiOutputStream extends OutputStream {
  OutputStream[] outputStreams;
  public MultiOutputStream(OutputStream... outputStreams) {
    this.outputStreams = outputStreams;
  }
  @Override
  public void write(int b) throws IOException {
    for (OutputStream out : outputStreams)
        out.write(b);
  }
  @Override
  public void write(byte[] b) throws IOException {
    for (OutputStream out : outputStreams)
        out.write(b);
  }
  @Override
  public void write(byte[] b, int off, int len) throws IOException {
    for (OutputStream out : outputStreams)
        out.write(b, off, len);
  }
  @Override
  public void flush() throws IOException {
    for (OutputStream out : outputStreams)
        out.flush();
  }
  @Override
  public void close() throws IOException {
    for (OutputStream out : outputStreams)
        out.close();
  }
}