blob: 089865b829be97e2994082e03374b905d9c15477 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.carbondata.common.logging.impl;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.log4j.Level;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
/**
* Copied from log4j to remove the hard coding for the file name from it Copied
* form log4j and modified for renaming files
*/
public class ExtendedRollingFileAppender extends RollingFileAppender {
private static final String DATE_FORMAT_FOR_TRANSFER = "yyyy-MM-dd'_'HH-mm-ss";
protected int currentLevel = Level.FATAL_INT;
/**
* Now in log file rolling(after file size
* exceeded the threshold) and deletion (after file count exceeded the file
* count threshold) it will print log message
*/
private long nextRollover = 0;
private volatile boolean cleanupInProgress = false;
/**
* Total number of files at any point of time should be Backup number of
* files + current file
*/
private static void cleanLogs(final String startName, final String folderPath,
int maxBackupIndex) {
final String fileStartName = startName.toLowerCase(Locale.US);
// Delete the oldest file, to keep Windows happy.
File file = new File(folderPath);
if (file.exists()) {
File[] files = file.listFiles(new FileFilter() {
public boolean accept(File file) {
if (!file.isDirectory() && file.getName().toLowerCase(Locale.US)
.startsWith(fileStartName)) {
return true;
}
return false;
}
});
if (null == files) {
return;
}
int backupFiles = files.length - 1;
if (backupFiles <= maxBackupIndex) {
return;
}
// Sort the file based on its name.
TreeMap<String, File> sortedMap = new TreeMap<String, File>();
for (File file1 : files) {
sortedMap.put(file1.getName(), file1);
}
// Remove the first log file from map. it will be <startName>.log
// itself which will be backed up in rollover
sortedMap.remove(sortedMap.firstKey());
Iterator<Entry<String, File>> it = sortedMap.entrySet().iterator();
Entry<String, File> temp = null;
// After clean up the files should be maxBackupIndex -1 number of
// files. Because one more backup file
// will be created after this method call is over
while (it.hasNext() && backupFiles > maxBackupIndex) {
temp = it.next();
File deleteFile = temp.getValue();
// Delete the file
// After deletion of log file it
// will print the log message in ReportService.log
if (deleteFile.delete()) {
backupFiles--;
} else {
LogLog.error("Couldn't delete file :: " + deleteFile.getPath());
}
}
}
}
/**
* Copied from log4j to remove hardcoding of file name
*/
public void rollOver() {
File target;
File file = new File(fileName);
String fileStartName = file.getName();
int dotIndex = fileStartName.indexOf('.');
if (dotIndex != -1) {
fileStartName = fileStartName.substring(0, dotIndex);
}
final String startName = fileStartName;
final String folderPath = file.getParent();
if (qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
LogLog.debug("rolling over count=" + size);
// if operation fails, do not roll again until
// maxFileSize more bytes are written
nextRollover = size + maxFileSize;
}
LogLog.debug("maxBackupIndex=" + maxBackupIndex);
boolean renameSucceeded = true;
// If maxBackups <= 0, then there is no file renaming to be done.
if (maxBackupIndex > 0) {
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_FOR_TRANSFER);
StringBuilder buffer = new StringBuilder();
String extension = "";
if (fileName.contains(".")) {
extension = fileName.substring(fileName.lastIndexOf("."));
buffer.append(fileName.substring(0, fileName.lastIndexOf(".")));
} else {
buffer.append(fileName);
}
buffer.append("_").append(dateFormat.format(new Date())).append(extension);
// Rename fileName to fileName.1
target = new File(buffer.toString());
this.closeFile(); // keep windows happy.
LogLog.debug("Renaming file " + file + " to " + target);
renameSucceeded = file.renameTo(target);
//
// if file rename failed, reopen file with append = true
//
if (!renameSucceeded) {
try {
this.setFile(fileName, true, bufferedIO, bufferSize);
} catch (InterruptedIOException e) {
Thread.currentThread().interrupt();
} catch (IOException e) {
LogLog.error("setFile(" + fileName + ", true) call failed.", e);
}
}
}
//
// if all renames were successful, then
//
if (renameSucceeded) {
try {
// This will also close the file. This is OK since multiple
// close operations are safe.
this.setFile(fileName, false, bufferedIO, bufferSize);
nextRollover = 0;
} catch (InterruptedIOException e) {
Thread.currentThread().interrupt();
} catch (IOException e) {
LogLog.error("setFile(" + fileName + ", false) call failed.", e);
}
}
// Do clean up finally
if (!cleanupInProgress) {
cleanUpLogs(startName, folderPath);
}
}
private void cleanUpLogs(final String startName, final String folderPath) {
if (maxBackupIndex > 0) {
// Clean the logs files
Runnable r = new Runnable() {
public void run() {
synchronized (ExtendedRollingFileAppender.class) {
cleanupInProgress = true;
try {
cleanLogs(startName, folderPath, maxBackupIndex);
} catch (Throwable e) {
// ignore any error
LogLog.error("Cleaning logs failed", e);
} finally {
cleanupInProgress = false;
}
}
}
};
Thread t = new Thread(r);
t.start();
}
}
protected void subAppend(LoggingEvent event) {
if (event.getLevel().toInt() <= currentLevel) {
super.subAppend(event);
if (fileName != null && qw != null) {
long size = ((CountingQuietWriter) qw).getCount();
if (size >= maxFileSize && size >= nextRollover) {
rollOver();
}
}
}
}
}