package com.equalexperts.logging.impl; import com.equalexperts.logging.LogMessage; import java.io.IOException; import java.nio.channels.FileLock; import java.util.concurrent.CountDownLatch; /** * Writes batches of log records to a path. * * A file lock is acquired and held during the batch and released afterwards. * This allows external log rotation to work. * @param <T> */ public class PathDestination<T extends Enum<T> & LogMessage> implements Destination<T>, ActiveRotationSupport { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); private final FileChannelProvider provider; private final StackTraceProcessor processor; private final ActiveRotationRegistry registry; private FileChannelProvider.Result currentChannel; private FileLock currentLock; private volatile CountDownLatch latch = new CountDownLatch(0); public PathDestination(FileChannelProvider provider, StackTraceProcessor processor, ActiveRotationRegistry registry) { this.provider = provider; this.processor = processor; this.registry = registry; } @Override public void beginBatch() throws Exception { closeAnyOpenBatch(); latch = new CountDownLatch(1); currentChannel = provider.getChannel(); currentLock = currentChannel.channel.lock(); } @Override public void publish(LogicalLogRecord<T> record) throws Exception { String physicalRecord = record.format(processor); currentChannel.writer.write(physicalRecord + LINE_SEPARATOR); //one call avoids a partial flush } @Override public void endBatch() throws Exception { closeAnyOpenBatch(); } private void closeAnyOpenBatch() throws IOException { latch.countDown(); if (currentChannel != null) { currentChannel.writer.flush(); currentLock.release(); currentChannel.writer.close(); currentLock = null; currentChannel = null; } } @Override public void close() throws Exception { closeAnyOpenBatch(); registry.remove(this); } @Override public void refreshFileHandles() throws InterruptedException { latch.await(); } public FileChannelProvider getProvider() { return provider; } @Override public StackTraceProcessor getStackTraceProcessor() { return processor; } public ActiveRotationRegistry getActiveRotationRegistry() { return registry; } }