Yosefarr
Yosefarr

Reputation: 797

Java: creating file every number of seconds

I'm using ScheduledThreadPoolExecutor to create a file every fileIntervalInSeconds seconds:

executorService = new ScheduledThreadPoolExecutor(1);
        executorService.scheduleAtFixedRate(new Runnable()
        {
            @Override
            public void run()
            {
                    File file = new File(fileName);
                    if (file.exists())
                    {
                        Log.debug("creating new file");
                        openFileWriter(file);
                    }

            }
        }, fileIntervalInSeconds, fileIntervalInSeconds, TimeUnit.SECONDS);
    }
private void openFileWriter() throws FileSystemNotificationException
{

        // 1 - close exist writer
        writer.close();
        // 2 - rename to backup file name
          ...
        // 3 - create new file      
        FileWriter writerFile = new FileWriter(fileName, true);
        writer = new PrintWriter(writerFile);

}

And i'm writing alert Messages to the file all the time:

private synchronized void writeLine(String line) throws InterruptedException
{
    writer.println(line);
}

My problem is:

  1. how can i ensure that i'm using writer when it is not closed? (writer.close())
  2. How can i wait to the ScheduledThreadPoolExecutor to finish creating the file before start writing

Upvotes: 3

Views: 2117

Answers (4)

TwoThe
TwoThe

Reputation: 14309

How about having a separate thread that handles the logging instead of that rather complicated construct?

public class Logger extends Thread {

private final LinkedBlockingQueue<String> linesToWrite = new LinkedBlockingQueue<>();
private final String filename;

private Logger(String filename) {
  super("Logging thread");
  this.filename = filename;
  this.setDaemon(true);
  this.setPriority(Thread.MIN_PRIORITY);
}

@Override
public void run() {
  try (BufferedWriter out = new BufferedWriter(new FileWriter(filename, true))) {

    String line;
    while (this.isInterrupted() == false) {
      line = linesToWrite.take();
      out.write(line);
      out.newLine();
      out.flush();
    }
  } catch (InterruptedException e) {
  } catch (IOException ex) {
    System.out.println("Failed to access log file: " + ex);
  }
}

public void log(final String line) {
  this.linesToWrite.add(line);
}

Then initialize the logger once:

final Logger logger = new Logger("test.log");
logger.start();

And then you can use it from anywhere in a thread-safe way like this:

logger.log("Test message");

You don't need to stop the logger, because Java will ensure with the try construct that the file is properly closed. However if you want, you can stop it like this:

logger.interrupt();

Now you can do all file manipulation in a single-threaded way, because there is only one thread accessing the log files ever at any time.

Upvotes: 0

OldCurmudgeon
OldCurmudgeon

Reputation: 65889

You have two alternatives:

  1. Create the new writer before closing the old one.
  2. Establish a lock before closing the writer which you check when writing.

Examples:

volatile PrintWriter writer;
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock writeLock = lock.writeLock();
Lock readLock = lock.readLock();

private void openFileWriterWithLock() throws IOException {

  if (writeLock.tryLock()) {
    try {
      // 1 - close exist writer
      writer.close();
      // 2 - rename to backup file name
      //...
      // 3 - create new file      
      FileWriter writerFile = new FileWriter(fileName, true);
      writer = new PrintWriter(writerFile);
    } finally {
      writeLock.unlock();
    }
  }

}

private synchronized void writeLineWithLock(String line) throws InterruptedException {
  readLock.lock();
  try {
    writer.println(line);
  } finally {
    readLock.unlock();
  }
}

private void openFileWriterWithoutLock() throws IOException {
  // 0. Note old file.
  PrintWriter oldWriter = writer;
  // 1. Create new file.
  FileWriter writerFile = new FileWriter(fileName, true);
  // 2. Swap the new one in.
  writer = new PrintWriter(writerFile);
  // 3 - close old writer
  oldWriter.close();
}

private synchronized void writeLineWithoutLock(String line) throws InterruptedException {
  writer.println(line);
}

Upvotes: 0

Dodd10x
Dodd10x

Reputation: 3349

You could simply create the new file every hour within your write method. You would have some slight overhead for the time check but that should be negligible. The example below will create a new log file every hour with the time in milliseconds added to the front of the file name. You can format the time however suits you.

public class LogWriter {
    private long lastCreationTime;
    PrintWriter writer;
    String logFileName;

    public LogWriter(String logFileName) {
        this.logFileName = logFileName;
        createLogFile(logFileName);
    }

    private void createLogFile(String fileName) {
        if(writer != null) {
            writer.close();
        }
        lastCreationTime = System.currentTimeMillis();
        FileWriter writerFile;
        try {
            writerFile = new FileWriter(lastCreationTime + "_" + fileName, true);
            writer = new PrintWriter(writerFile);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private synchronized void writeLine(String line) {
        if(lastCreationTime < System.currentTimeMillis() - 3600000) {
            createLogFile(logFileName);
        }
        writer.write(line);
    }
}

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533880

How about checking the file exists before you write to it. No need for a backrgound thread or synchronization

private synchronized void writeLine(String line) {
    if (!file.exists())
       reopenWritingFile();
    writer.println(line);
}

Upvotes: 4

Related Questions