简体   繁体   中英

How do I make multiple threads and classes write into the same log file in Java?

Within a java application, I have several Classes and Threads here and there. I would like them all to write their logs to one single file. How can I achieve that?

Also here's some more questions:

  1. Do I receive the same instance of a logger if I define the same name for them even in different classes. To be clear, are the following two loggers the same:

     class MyClass1 { Logger logger1 = Logger.getLogger("theSameName"); } class MyClass2{ Logger logger2 = Logger.getLogger("theSameName"); } 
  2. What happens if I define two of the FileHandler in two separate Threads that have the same file name? Do both of them write to the same file? If yes, what happens if the file is already opened by the first one, when the second one tries to open and write to it?

  3. What happens if I try to add the same FielHandler to a logger more than once as in:

     logger.addHandler(myFileHandler); // in Thread one ... logger.addHandler(myFileHandler); // in Thread two 

Will I receive a RuntimeException because of the two FileHandler may try to open the same file simultaneously?

I'm using java-util-logging not log4j .

Make a singleton log class so any other class or thread tries creating a copy of it, will actually be using the same instance.

Example implementation with 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;
    }
}

Then in your classes you will be using it like this:

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");
}

Notice that whatever you name the LogMe and its Logger in your classes it won't have any effect because they are referencing the same instance of LogMe and its Logger

Example output in the log file will look something like this:

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

Notice that the order of these log lines will depends on the order of execution of your classes. Also you won't need any synchronization, quoting JavaDoc of Logger:

All methods on Logger are multi-thread safe.

Do I receive the same instance of a logger if I define the same name for them even in different classes.

I don't believe it is defined anywhere, but most like it is the same object and it shouldn't matter. The libraries don't determine which file to write to based on Logger instances.

What happens if I define two FileHandlers in two separate Threads that have the same file name?

Most like you will get two logging handlers. This means everything will be logged twice unless you attempt to write to the same file in which case it is likely to get corrupted.

There is no good reason to do this. Just define your configuration on start up, once.

What happens if I try to add the same FielHandler to a logger more than once as in:

It is unlikely the underlying library will notice and it will corrupt the file. If you really need to know, I suggest you try it but don't do this in production.

Will I receive a RuntimeException or something because two FileHandler may try to open the same file simultaneously?

This is not what FileOutputStream does or the OS. You just get a corrupted file.

Create a singleton class that will be used by your application.

Here is everything you need to know when writing a singleton in Java: http://learnjava.today/2015/08/the-best-singleton-in-java/

Keep coding ;)

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!" );
    }
}

I have code that pushes data to multiple OutputStreams . I use it to duplicate System.out to both System.out and a FileOutputStream .

I would basically "reroll" that code so it use multiple input streams to pipe to one "master" file.

Here is my code

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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