All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qiniu.appender.QiniuLoggingGuard Maven / Gradle / Ivy

package com.qiniu.appender;

import com.qiniu.pandora.common.Constants;
import com.qiniu.pandora.pipeline.sender.DataSender;

import java.io.*;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Created by jemy on 2018/7/2.
 */
public class QiniuLoggingGuard {
    private static QiniuLoggingGuard instance;
    private DataSender dataSender;
    private FilenameFilter logFileFilter;
    private String logTag;
    private String logCacheDir;
    private int logRotateInterval; // in seconds
    private int logRetryInterval; // in seconds

    private Map retryingFiles;
    private ExecutorService retryService;
    private int currentLogCounter;
    private String currentLogFileName;
    private File currentLogFile;
    private long currentFileLength;
    private Calendar currentLogCalendar;
    private BufferedOutputStream currentLogWriter;
    private long logRotateIntervalInMillis;
    private long logRotateIntervalInMinutes;

    private QiniuLoggingGuard() {
        this.logTag = Configs.DefaultLogTag;
        this.logCacheDir = Configs.DefaultLogCacheDir;
        this.logRotateInterval = Configs.DefaultLogRotateInterval;
        this.logRetryInterval = Configs.DefaultLogRetryInterval;

        this.logRotateIntervalInMillis = this.logRotateInterval * 1000;
        this.logRotateIntervalInMinutes = this.logRotateInterval / 60;
        this.retryingFiles = new ConcurrentHashMap();
        this.currentFileLength = 0;
        this.currentLogCounter = 0;

        this.logFileFilter = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.contains(logTag);
            }
        };

        //create the backend task to put logs
        new Thread(new Runnable() {
            @Override
            public void run() {
                pushLogs();
            }
        }).start();
    }

    private void pushLogs() {
        while (true) {
            File logCacheDirFile = new File(this.logCacheDir);
            if (logCacheDirFile.exists()) {
                //filter all the log file
                File[] logFiles = logCacheDirFile.listFiles(this.logFileFilter);
                if (logFiles != null) {
                    for (File logFile : logFiles) {
                        if (logFile.getName().endsWith(".log.tmp")) {
                            //check file last modified
                            Date lastModifiedDate = new Date(logFile.lastModified());
                            //add log rotate time
                            Calendar rotateDate = Calendar.getInstance();
                            rotateDate.setTime(lastModifiedDate);
                            if (rotateDate.getTimeInMillis() < System.currentTimeMillis()) {
                                //change it to log file with suffix .log
                                String logFilePath = logFile.getAbsolutePath();
                                File dstLogFile = new File(logFilePath.substring(0, logFilePath.length() - 4));
                                logFile.renameTo(dstLogFile);
                            }
                        }
                    }
                }

                //rescan the log files and upload
                File[] readyLogFiles = logCacheDirFile.listFiles(this.logFileFilter);
                if (readyLogFiles != null) {
                    for (final File logFile : readyLogFiles) {
                        if (logFile.getName().endsWith(".log")) {
                            final String logFileAbsPath = logFile.getAbsolutePath();
                            //check whether the last upload finishes
                            if (this.retryingFiles.containsKey(logFileAbsPath)) {
                                //wait for next scheduled time
                                continue;
                            }
                            this.retryingFiles.put(logFileAbsPath, true);
                            //try to upload the data
                            retryService.execute(new Runnable() {
                                @Override
                                public void run() {
                                    byte[] logBuffer = new byte[(int) logFile.length()];
                                    FileInputStream fs = null;
                                    try {
                                        fs = new FileInputStream(logFile);
                                        fs.read(logBuffer);
                                        dataSender.send(logBuffer);
                                        //delete the file when sent success
                                        logFile.delete();
                                    } catch (FileNotFoundException e) {
                                        //e.printStackTrace();
                                    } catch (IOException e) {
                                        //e.printStackTrace();
                                    } finally {
                                        if (fs != null) {
                                            try {
                                                fs.close();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }
                                    //whether success or not, delete key
                                    retryingFiles.remove(logFileAbsPath);
                                }
                            });
                        }
                    }
                }
            }
            //wait for the next run
            try {
                TimeUnit.SECONDS.sleep(this.logRetryInterval);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static QiniuLoggingGuard getInstance(int logRetryThreadPoolSize) {
        if (instance == null) {
            synchronized (QiniuLoggingGuard.class) {
                if (instance == null) {
                    instance = new QiniuLoggingGuard();
                    instance.retryService = Executors.newFixedThreadPool(logRetryThreadPoolSize);
                }
            }
        }
        return instance;
    }

    public void setDataSender(DataSender dataSender) {
        this.dataSender = dataSender;
    }

    public void setLogCacheDir(String logCacheDir) throws FileNotFoundException {
        this.logCacheDir = logCacheDir;
        File cacheDir = new File(this.logCacheDir);
        if (!cacheDir.exists() || !cacheDir.isDirectory() || !cacheDir.canWrite()) {
            throw new FileNotFoundException(this.logCacheDir + " must exists and be a writable directory");
        }
    }

    public void setLogRotateInterval(int logRotateInterval) {
        this.logRotateInterval = logRotateInterval;
        this.logRotateIntervalInMillis = this.logRotateInterval * 1000;
        this.logRotateIntervalInMinutes = this.logRotateInterval / 60;
    }

    public void setLogRetryInterval(int logRetryInterval) {
        this.logRetryInterval = logRetryInterval;
    }

    public synchronized void write(byte[] logBuffer) {
        int logBufferLen = logBuffer.length;
        //check rotate interval
        long currentTime = System.currentTimeMillis();
        Date currentDate = new Date(currentTime);
        Calendar currentCalendar = Calendar.getInstance();
        currentCalendar.setTime(currentDate);

        long currentLogMinute = currentCalendar.get(Calendar.MINUTE) / this.logRotateIntervalInMinutes *
                this.logRotateIntervalInMinutes;
        String currentLogFilePrefix = String.format("%04d_%02d_%02d_%02d_%02d", currentCalendar.get(Calendar.YEAR),
                currentCalendar.get(Calendar.MONTH), currentCalendar.get(Calendar.DAY_OF_MONTH),
                currentCalendar.get(Calendar.HOUR_OF_DAY), currentLogMinute);

        if (this.currentLogWriter == null) {
            //read from local disk when app restarted
            while (true) {
                //skip the old files counter
                String oldLogFileName = String.format("%s_%010d.%s.log", currentLogFilePrefix, this.currentLogCounter,
                        this.logTag);
                File oldLogFile = new File(this.logCacheDir, oldLogFileName);
                if (oldLogFile.exists()) {
                    this.currentLogCounter += 1;
                    continue;
                }

                break;//found a new log file
            }

            this.currentLogFileName = String.format("%s_%010d.%s.log.tmp", currentLogFilePrefix,
                    this.currentLogCounter, this.logTag);
            this.currentLogFile = new File(this.logCacheDir, this.currentLogFileName);
            boolean createNewLog = false;
            if (currentLogFile.exists()) {
                if (logBufferLen + this.currentLogFile.length() < Configs.DefaultLogRotateFileSize) {
                    try {
                        this.currentLogWriter = new BufferedOutputStream(new FileOutputStream(this.currentLogFile,
                                true));
                        this.currentFileLength = this.currentLogFile.length();
                        this.currentLogCalendar = currentCalendar;
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        //NOTE never happens
                    }
                } else {
                    this.currentLogCounter += 1;
                    createNewLog = true;
                }
            } else {
                createNewLog = true;
            }

            if (createNewLog) {
                //rename the old file
                String currentLogFilePath = this.currentLogFile.getAbsolutePath();
                File dstLogFile = new File(currentLogFilePath.substring(0, currentLogFilePath.length() - 4));
                this.currentLogFile.renameTo(dstLogFile);

                //create new file
                this.currentLogFileName = String.format("%s_%010d.%s.log.tmp", currentLogFilePrefix,
                        this.currentLogCounter, this.logTag);
                this.currentLogFile = new File(this.logCacheDir, this.currentLogFileName);

                try {
                    this.currentLogWriter = new BufferedOutputStream(new FileOutputStream(this.currentLogFile));
                    this.currentFileLength = 0;
                    this.currentLogCalendar = currentCalendar;
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    //NOTE never happens
                }
            }
        }

        //when log writer not null, check the file size before write the log
        //when log writer not null, check the log rotate before write the log
        boolean rotateBySize = logBufferLen + this.currentFileLength > Configs.DefaultLogRotateFileSize;
        boolean rotateByInterval = currentCalendar.getTimeInMillis() - this.currentLogCalendar.getTimeInMillis()
                > this.logRotateIntervalInMillis;
        if (rotateBySize || rotateByInterval) {
            if (rotateByInterval) {
                this.currentLogCounter = 0;
            } else {
                this.currentLogCounter += 1;
            }

            //close the old writer and create a new one
            try {
                this.currentLogWriter.flush();
                this.currentLogWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
                //NOTE ignore
            }

            //change tmp log file to finished log file
            String currentLogFilePath = this.currentLogFile.getAbsolutePath();
            File dstLogFile = new File(currentLogFilePath.substring(0, currentLogFilePath.length() - 4));
            this.currentLogFile.renameTo(dstLogFile);

            //create new log file
            this.currentLogFileName = String.format("%s_%010d.%s.log.tmp", currentLogFilePrefix,
                    this.currentLogCounter, this.logTag);
            this.currentLogFile = new File(this.logCacheDir, this.currentLogFileName);
            try {
                this.currentLogWriter = new BufferedOutputStream(new FileOutputStream(this.currentLogFile));
                this.currentFileLength = 0;
                this.currentLogCalendar = currentCalendar;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                //NOTE ignore
            }
        }

        //write log buffer to file
        try {
            this.currentLogWriter.write(logBuffer);
            this.currentLogWriter.flush();
            this.currentFileLength += logBufferLen;
        } catch (IOException e) {
            e.printStackTrace();
            //NOTE write error, output the log buffer to console
            System.out.println(new String(logBuffer, Constants.UTF_8));
        }
    }

    public synchronized void close() {
        try {
            if (this.currentLogWriter != null) {
                this.currentLogWriter.flush();
                this.currentLogWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy