package eu.fbk.knowledgestore.filestore;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.fbk.knowledgestore.data.Stream;
import eu.fbk.knowledgestore.internal.Util;
/**
* A {@code FileStore} wrapper that log calls to the operations of a wrapped {@code FileStore} and
* their execution times.
* <p>
* This wrapper intercepts calls to an underlying {@code FileStore} and logs request information
* and execution times via SLF4J (level DEBUG, logger named after this class). The overhead
* introduced by this wrapper when logging is disabled is negligible.
* </p>
*/
public class LoggingFileStore extends ForwardingFileStore {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFileStore.class);
private final FileStore delegate;
/**
* Creates a new instance for the wrapped {@code FileStore} specified.
*
* @param delegate
* the wrapped {@code FileStore}
*/
public LoggingFileStore(final FileStore delegate) {
this.delegate = Preconditions.checkNotNull(delegate);
LOGGER.debug("{} configured", getClass().getSimpleName());
}
@Override
protected FileStore delegate() {
return this.delegate;
}
@Nullable
private <T> Stream<T> logClose(@Nullable final Stream<T> stream, final String name,
final long ts) {
final AtomicLong count = new AtomicLong(0);
final AtomicBoolean eof = new AtomicBoolean(false);
return stream.track(count, eof).onClose(new Runnable() {
@Override
public void run() {
LOGGER.debug("{} - {} closed after {} ms, {} files, eof={}",
LoggingFileStore.this, name, System.currentTimeMillis() - ts, count, eof);
}
});
}
@Override
public void init() throws IOException {
if (LOGGER.isDebugEnabled()) {
final long ts = System.currentTimeMillis();
super.init();
LOGGER.debug("{} - initialized in {} ms", this, System.currentTimeMillis() - ts);
} else {
super.init();
}
}
@Override
public InputStream read(final String filename) throws FileMissingException, IOException {
if (LOGGER.isDebugEnabled()) {
final long ts = System.currentTimeMillis();
final InputStream result = Util.interceptClose(super.read(filename), new Runnable() {
@Override
public void run() {
LOGGER.debug("{} - {} input stream closed in {} ms", LoggingFileStore.this,
filename, System.currentTimeMillis() - ts);
}
});
LOGGER.debug("{} - {} opened for read in {} ms", this, filename,
System.currentTimeMillis() - ts);
return result;
} else {
return super.read(filename);
}
}
@Override
public OutputStream write(final String filename) throws FileExistsException, IOException {
if (LOGGER.isDebugEnabled()) {
final long ts = System.currentTimeMillis();
final OutputStream result = Util.interceptClose(super.write(filename), new Runnable() {
@Override
public void run() {
LOGGER.debug("{} - {} output stream closed in {} ms", LoggingFileStore.this,
filename, System.currentTimeMillis() - ts);
}
});
LOGGER.debug("{} - {} opened for write in {} ms", this, filename,
System.currentTimeMillis() - ts);
return result;
} else {
return super.write(filename);
}
}
@Override
public void delete(final String filename) throws FileMissingException, IOException {
if (LOGGER.isDebugEnabled()) {
final long ts = System.currentTimeMillis();
super.delete(filename);
LOGGER.debug("{} - {} deleted in {} ms", this, filename, //
System.currentTimeMillis() - ts);
} else {
super.delete(filename);
}
}
@Override
public Stream<String> list() throws IOException {
if (LOGGER.isDebugEnabled()) {
final String name = "list() result stream";
final long ts = System.currentTimeMillis();
final Stream<String> result = logClose(super.list(), name, ts);
LOGGER.debug("{} - {} obtained in {} ms", this, name, System.currentTimeMillis() - ts);
return result;
} else {
return super.list();
}
}
@Override
public void close() {
if (LOGGER.isDebugEnabled()) {
final long ts = System.currentTimeMillis();
super.close();
LOGGER.debug("{} - closed in {} ms", this, System.currentTimeMillis() - ts);
} else {
super.close();
}
}
}