logback可以把N天的日志压缩成一个包(zip,gz),在压缩的时候logback采用后台异步线程的方式来实现,下面咱们就来看看Future接口在logback中的妙用。
先来看下Future接口的全貌吧。
public interface Future<V> {
//取消任务
boolean cancel(boolean mayInterruptIfRunning);
// 任务如果被取消就返回true
boolean isCancelled();
//如果任务完成就返回true
boolean isDone();
// 获取线程的执行结果,如果任务没有执行完成就会一直等待
V get() throws InterruptedException, ExecutionException;
//获取任务的执行结果,可以指定一个单位时间,如果在规定的时间内没有完成任务,那么就会抛出TimeoutException异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
/**
gz压缩方法,用于压缩成.gz文件
*/
private void gzCompress(String nameOfFile2gz, String nameOfgzedFile) {
File file2gz = new File(nameOfFile2gz);
if (!file2gz.exists()) {
addStatus(new WarnStatus("The file to compress named [" + nameOfFile2gz + "] does not exist.", this));
return;
}
if (!nameOfgzedFile.endsWith(".gz")) {
nameOfgzedFile = nameOfgzedFile + ".gz";
}
File gzedFile = new File(nameOfgzedFile);
if (gzedFile.exists()) {
addWarn("The target compressed file named [" + nameOfgzedFile + "] exist already. Aborting file compression.");
return;
}
addInfo("GZ compressing [" + file2gz + "] as [" + gzedFile + "]");
createMissingTargetDirsIfNecessary(gzedFile);
BufferedInputStream bis = null;
GZIPOutputStream gzos = null;
try {
bis = new BufferedInputStream(new FileInputStream(nameOfFile2gz));
gzos = new GZIPOutputStream(new FileOutputStream(nameOfgzedFile));
byte[] inbuf = new byte[BUFFER_SIZE];
int n;
while ((n = bis.read(inbuf)) != -1) {
gzos.write(inbuf, 0, n);
}
bis.close();
bis = null;
gzos.close();
gzos = null;
if (!file2gz.delete()) {
addStatus(new WarnStatus("Could not delete [" + nameOfFile2gz + "].", this));
}
} catch (Exception e) {
addStatus(new ErrorStatus("Error occurred while compressing [" + nameOfFile2gz + "] into [" + nameOfgzedFile + "].", this, e));
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
// ignore
}
}
if (gzos != null) {
try {
gzos.close();
} catch (IOException e) {
// ignore
}
}
}
}
//工厂方法,可以根据传入的类型来判断是使用gz,还是zip压缩
public void compress(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) {
switch (compressionMode) {
case GZ:
gzCompress(nameOfFile2Compress, nameOfCompressedFile);
break;
case ZIP:
zipCompress(nameOfFile2Compress, nameOfCompressedFile, innerEntryName);
break;
case NONE:
throw new UnsupportedOperationException("compress method called in NONE compression mode");
}
}
logback 把压缩的任务交给了一个线程去执行
//开启一个线程用于压缩日志文件
public Future<?> asyncCompress(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) throws RolloverFailure {
CompressionRunnable runnable = new CompressionRunnable(nameOfFile2Compress, nameOfCompressedFile, innerEntryName);
ExecutorService executorService = context.getExecutorService();
Future<?> future = executorService.submit(runnable);
return future;
}
//压缩线程
class CompressionRunnable implements Runnable {
final String nameOfFile2Compress;
final String nameOfCompressedFile;
final String innerEntryName;
public CompressionRunnable(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) {
this.nameOfFile2Compress = nameOfFile2Compress;
this.nameOfCompressedFile = nameOfCompressedFile;
this.innerEntryName = innerEntryName;
}
public void run() {
Compressor.this.compress(nameOfFile2Compress, nameOfCompressedFile, innerEntryName);
}
}
stop方法才是重头戏,当调用stop方法的时候,需要检查有没有线程在正在进行压缩工作,可以看出logback使用Future的特性就是检测某个后台任务是否在指定时间内完成任务。
@Override
public void stop() {
if (!isStarted())
return;
//检查任务是否完成
//压缩任务
waitForAsynchronousJobToStop(compressionFuture, "compression");
//clean任务
waitForAsynchronousJobToStop(cleanUpFuture, "clean-up");
super.stop();
}
private void waitForAsynchronousJobToStop(Future<?> aFuture, String jobDescription) {
if (aFuture != null) {
try {
//获取执行结果
aFuture.get(CoreConstants.SECONDS_TO_WAIT_FOR_COMPRESSION_JOBS, TimeUnit.SECONDS);
} catch (TimeoutException e) {
addError("Timeout while waiting for " + jobDescription + " job to finish", e);
} catch (Exception e) {
addError("Unexpected exception while waiting for " + jobDescription + " job to finish", e);
}
}
}