Add maximum size for logs folder
Feature request for setting not only the log file maximum size, but a folder maximum size, and delete old logs when this maximum size is reached. This feature will provide a cleaner disk logging.
This sounds a reasonable idea. I'd be happy to improve disk log feature a little more definitely.
I am implementing something myself, but I am not sure if the solution is ideal. Also I am not sure in which class it should be. Maybe the same class where the log file size is checked?
- I check the folder size by checking all the files in it
- logfilename_0.csv 500kb
- logfilename_1.csv 500kb
- logfilename_2.csv 500kb
- logfilename_3.csv 500kb
- If the size is over the maximum size, I look for the oldest log file.
- I delete this file (imagine it is the logfilename_0)
- logfilename_1.csv 500kb
- logfilename_2.csv 500kb
- logfilename_3.csv 500kb
- Then the class checks for the right log file to write logs starting wiht logfilename_0, as it does not exist it will create a new one. The next time the folder size is over maximum, ith will delete logfilename_1 as it is the oldest.
- logfilename_0.csv 4kb
- logfilename_1.csv 500kb
- logfilename_2.csv 500kb
- logfilename_3.csv 500kb
This wouldbe a circular log that keeps the newer logs. Other things to consider are:
- Do we allow the user to choose any maximum folder size?
- Do we allow the use to use unlimited folder size?
- If the user uses diferent log file names for differente modules with differtent DiskLogHandlers, in the folder we will have the following:
- logfilename_app1_0.csv 500kb
- logfilename_app1_1.csv 500kb
- logfilename_app1_2.csv 500kb
- logfilename_module_0.csv 500kb
- logfilename_module_1.csv 500kb
Do we set the max folder size considering all different logs? Then which one do we delete?
I already have my circular logging working using a custom DiskLogHandler. This is not a robust solution yet, but I will paste my solution below so you get the idea.
public class DiskLogHandler extends Handler{
private final String folder;
private final int maxFileSize;
private final String fileName;
private final int maxFolderSize;
/**
* Creates a disk log handler
*
* @param folder The folder for logs
* @param fileName The log file basename
* @param maxFileSize Maximum file size
* @param maxFolderSize Maximum folder size. Must be multiple of file. TODO: check this premise
*/
public DiskLogHandler(String folder, String fileName, int maxFileSize, int maxFolderSize) {
this(getDefaultLooper(), folder, fileName, maxFileSize, maxFolderSize);
}
public DiskLogHandler(Looper looper, String folder, String fileName, int maxFileSize, int maxFolderSize) {
super(looper);
this.folder = folder;
this.fileName = fileName;
this.maxFileSize = maxFileSize;
this.maxFolderSize = maxFolderSize;
}
private static Looper getDefaultLooper() {
HandlerThread ht = new HandlerThread("AndroidFileLogger");
ht.start();
return ht.getLooper();
}
@SuppressWarnings("checkstyle:emptyblock")
@Override
public void handleMessage(Message msg) {
String content = (String) msg.obj;
FileWriter fileWriter = null;
File logFile = getLogFile(folder, fileName);
try {
fileWriter = new FileWriter(logFile, true);
writeLog(fileWriter, content);
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
if (fileWriter != null) {
try {
fileWriter.flush();
fileWriter.close();
} catch (IOException e1) { /* fail silently */ }
}
}
}
/**
* This is always called on a single background thread.
* Implementing classes must ONLY write to the fileWriter and nothing more.
* The abstract class takes care of everything else including close the stream and catching IOException
*
* @param fileWriter an instance of FileWriter already initialised to the correct file
*/
private void writeLog(FileWriter fileWriter, String content) throws IOException {
fileWriter.append(content);
}
/**
* Gets the right log file to write in, checking current log max size and folder max size.
*
* @param folderName
* @param fileName
* @return
*/
private File getLogFile(String folderName, String fileName) {
File folder = new File(folderName);
if (!folder.exists()) {
//TODO: What if folder is not created, what happens then?
folder.mkdirs();
}
int newFileCount = 0;
File newFile;
File existingFile = null;
checkFolderSize(folder, fileName);
newFile = new File(folder, String.format("%s_%s.csv", fileName, newFileCount));
while (newFile.exists()) {
existingFile = newFile;
newFileCount++;
newFile = new File(folder, String.format("%s_%s.csv", fileName, newFileCount));
// This block is needed when we delete an old log and create a new one.
if(existingFile.length() <= maxFileSize){
break;
}
}
if (existingFile != null) {
if (existingFile.length() >= maxFileSize) {
return newFile;
}
return existingFile;
}
return newFile;
}
/**
* If folder size is bigger than maximum size deletes the older log file.
*/
private void checkFolderSize(File folder, String fileName){
if(folderSize(folder) >= this.maxFolderSize){
deleteOlderLog(folder);
}
}
/**
* Checks all files in folder and deletes the oldest modified.
*
* @param folder The target folder to check files
*/
private void deleteOlderLog(File folder){
long lastModified = Long.MAX_VALUE;
File choice = null;
for (File file : folder.listFiles()) {
if (file.isFile()) {
if(file.lastModified() < lastModified){
choice = file;
lastModified = file.lastModified();
}
}
}
if(choice != null){
choice.delete();
}
}
/**
* Returns folder size after counting all files inside
*
* @param folder The target folder to check
* @return The size of the folder
*/
private static long folderSize(File folder) {
long folderSize = 0;
if(folder.listFiles() == null){
return 0;
}
for (File file : folder.listFiles()) {
if (file.isFile())
folderSize += file.length();
else
folderSize += folderSize(file);
}
return folderSize;
}
}
how to use,please?