logger icon indicating copy to clipboard operation
logger copied to clipboard

Add maximum size for logs folder

Open jbarbadillo opened this issue 7 years ago • 4 comments

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.

jbarbadillo avatar Apr 11 '18 07:04 jbarbadillo

This sounds a reasonable idea. I'd be happy to improve disk log feature a little more definitely.

orhanobut avatar Apr 11 '18 12:04 orhanobut

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?

jbarbadillo avatar Apr 11 '18 17:04 jbarbadillo

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;
    }
}

jbarbadillo avatar Apr 12 '18 08:04 jbarbadillo

how to use,please?

BlissYang91 avatar Nov 29 '18 08:11 BlissYang91