Android System Log Write File

Keywords: Android github Mobile Hibernate

Catalogue introduction

  • 1. Business requirements
  • 2. What are the current practices and problems?
  • 3. Customize log Tool Class, Print log and Write Files
  • 4. Open thread pool in application for log printing
  • 5. Write a service to write system logs to files

About links

1. Business requirements

  • Requires that app system logs be written to files in the specified file directory
  • Automatically switch to the next folder after writing files to a certain size
  • Require app to continuously write logs to files at runtime
  • Requirements can clear log files 7 or n days ago
  • Require sd card, write log to sd card file; no write to memory card; if no sd card to sd card, then you can switch paths
  • For code addresses, you can refer to: https://github.com/yangchong211/YCAudioPlayer

2. What are the current practices and problems?

  • 1. Customize the log tool class to write the output log to the folder when the log is output. However, only the status log in priority5 can be recorded
  • 2. Open the thread pool in application, and then call the setPrint method in the PrintToFileUtil class (tool class written by myself). Problem: app is always on, it can't print continuously, and it hasn't found the reason yet.
  • 3. When opening app, open service service service. When app process is not killed or service is not destroyed, it will always run in the background, so it can also handle printing log logic. And by broadcasting, the sd state can be monitored to achieve the goal of switching log paths.

3. Customize log Tool Class, Print log and Write Files

  • 3.1 The direct invocation code is as follows:
public static void e(String msg) {
    if (isDebug){
        Log.e(TAG, msg);
        PrintToFileUtil.input2File(msg,"path");
    }
}
  • 3.2 Tool classes are as follows
/**
 * Write the content directly in the file and set the path by yourself.
 * This is to print the log and write it to the file at the same time.
 * It is not recommended to write directly to a new sub-thread. It is recommended to open the thread pool to avoid thread wastage everywhere.
 * @param input                     Write contents
 * @param filePath                  Route
 * @return
 */
static boolean input2File(final String input, final String filePath) {
    if (sExecutor == null) {
        sExecutor = Executors.newScheduledThreadPool(5);
    }
    Future<Boolean> submit = sExecutor.submit(new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            BufferedWriter bw = null;
            try {
                // Construct a FileWriter object with a given file name and use Boolean values to indicate whether additional data is written.
                FileWriter fileWriter = new FileWriter(filePath, true);
                bw = new BufferedWriter(fileWriter);
                bw.write(input);
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            } finally {
                try {
                    if (bw != null) {
                        bw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    try {
        return submit.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    return false;
}

4. Open thread pool in application for log printing

/**
 * Problems encountered:
 * 1.When will this method be called?
 * 2.Why does calling this method cause the program to be unresponsive and where is the card?
 * 3.How to Avoid Thread Blockage
 * 4.How to switch to the next. txt file after printing to a certain log?
 * @param logPath
 */
public static void setPrint(String logPath){
    if (sExecutor == null) {
        sExecutor = Executors.newScheduledThreadPool(5);
    }

    //Name the file as xx month and xx day, so that every day's file is different
    Date now = new Date(System.currentTimeMillis());
    String format = FORMAT.format(now);
    String date = format.substring(0, 5);
    final String newLogPath = logPath +date;

    Future<Boolean> submit = sExecutor.submit(new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            /*Core code*/
            while(captureLogThreadOpen){
                /*Order preparation*/
                ArrayList<String> getLog = new ArrayList<>();
                getLog.add("logcat");
                getLog.add("-d");
                getLog.add("-v");
                getLog.add("time");
                ArrayList<String> clearLog = new ArrayList<>();
                clearLog.add("logcat");
                clearLog.add("-c");
                try{
                    //Crawl the current cache log
                    Process process = Runtime.getRuntime().exec(getLog.toArray(new String[getLog.size()]));
                    //Get the input stream
                    BufferedReader buffRead = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    //Clearance is for the purpose that the next crawl will not be scratched from scratch.
                    Runtime.getRuntime().exec(clearLog.toArray(new String[clearLog.size()]));


                    String str = null;
                    //Open file
                    File logFile = new File(newLogPath + "log.txt");
                    //true means appending at the end of a document when it is written
                    FileOutputStream fos = new FileOutputStream(logFile, true);
                    //Line-wrapping strings
                    String newline = System.getProperty("line.separator");
                    //Date date = new Date(System.currentTimeMillis());
                    //String time = format.format(date);

                    //Log.i(TAG, "thread");
                    //Printing device information
                    fos.write(printDeviceInfo().getBytes());

                    while((str=buffRead.readLine())!=null){         //Loop read each line
                        //Runtime.getRuntime().exec(clearLog.toArray(new String[clearLog.size()]));
                        //Log.i(TAG, str);
                        @SuppressLint("SimpleDateFormat")
                        Date date = new Date(System.currentTimeMillis());
                        String time = FORMAT.format(date);
                        //Plus last year
                        fos.write((time + str).getBytes());
                        //Line feed
                        fos.write(newline.getBytes());
                        logCount++;
                        //Exit more than 100,000 lines
                        if(logCount>100000){
                            captureLogThreadOpen = false;
                            fos.close();
                            break;
                        }
                    }
                    fos.close();
                    String[] strings = clearLog.toArray(new String[clearLog.size()]);
                    Runtime.getRuntime().exec(strings);
                    return true;
                }catch(Exception e){
                    e.printStackTrace();
                    return false;
                }
            }
            return true;
        }
    });


    try {
        submit.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

5. Write a service to write system logs to files

  • 5.1 Document Points

    • 1. Support customizing the path and file name of log writing;
    • 2. If there is an sd card, it will be recorded in the sd card; otherwise, it will be recorded in the memory card.
    • 3. If the previous log is recorded in the memory card and the sd card is later, the files in the previous memory will be copied into the SDCard.
    • 4. sd card loading and unloading broadcasting can be monitored to switch the save path of the log.
    • 5. The longest period of log storage can be set in SD card, which is currently set to 7 days.
    • 6. When the file written to the log is larger than the custom maximum (currently 10M), it can be automatically switched to the next folder.
    • 7. Supports clearing log caches. Clear the log caches before recording each log, otherwise duplicate logs will be recorded in two log files.
    • 8. Logs collected include: d, v, e, w, c
    • 9. Supports automatic deletion of files after exceeding the maximum save date. The deletion logic is to compare the name of the file named by the date with the current time.
    • 10. Open the service when the app opens, and the service keeps a log and writes to a file at run time unless the service is killed.
    • 11. It is recommended that log names be named by date to facilitate deletion logic.
  • 5.2 Specific Implementation Code

    • 5.2.1 Register in List File
     <service android:name=".service.LogService" />
    
    <!--System log permissions-->
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    • 5.2.2 The logService code is as follows
public class LogService extends Service {

    //Journal
    private static final String TAG = "LogService";

    //Maximum in-memory log file, 10M
    private static final int MEMORY_LOG_FILE_MAX_SIZE = 10 * 1024 * 1024;
    //In-memory log file size monitoring interval, 10 minutes
    private static final int MEMORY_LOG_FILE_MONITOR_INTERVAL = 10 * 60 * 1000;
    //The maximum number of days to save log files in sd card
    private static final int SDCARD_LOG_FILE_SAVE_DAYS = 7;
    //The path of the log file in memory (the path of the log file in the installation directory)
    private String LOG_PATH_MEMORY_DIR;
    //The path of log files in sdcard
    private String LOG_PATH_SDCARD_DIR;

    //The current logging type is stored under the SD card
    private final int SDCARD_TYPE = 0;
    //The current logging type is stored in memory
    private final int MEMORY_TYPE = 1;
    //Current log record type, default SD card
    private int CURR_LOG_TYPE = SDCARD_TYPE;
    //If the current log is written in memory, record the current log file name
    private String CURR_INSTALL_LOG_NAME;
    //The name of the log file output by this service can also be in txt format, or the suffix name is. log.
    private String logServiceLogName = "Log.log";

    @SuppressLint("SimpleDateFormat")
    private SimpleDateFormat myLogSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //Log Name Format
    @SuppressLint("SimpleDateFormat")
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH time mm branch ss second");

    private OutputStreamWriter writer ;
    private Process process;

    //awaken
    private PowerManager.WakeLock wakeLock;

    //SDCard status monitoring, broadcast monitoring sd card dynamics, such as users unplug sd card
    private SDStateMonitorReceiver sdStateReceiver;
    private LogTaskReceiver logTaskReceiver;

    /* Is the log file size being monitored?
     * If the current log record is false in SDAD
     * true if the current log record is in memory*/
    private boolean logSizeMoniting = false;

    //Log file monitoring action
    private static String MONITOR_LOG_SIZE_ACTION = "MONITOR_LOG_SIZE";
    //Switching log file action
    private static String SWITCH_LOG_FILE_ACTION = "SWITCH_LOG_FILE_ACTION";

    /**
     * No binding required
     * @param intent            intent
     * @return                  IBinder object
     */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    /**
     * Call this method when destroyed
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        recordLogServiceLog("LogService onDestroy");
        if (writer != null) {
            try {
                writer.close();
                writer = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (process != null) {
            process.destroy();
        }

        //Note the need to cancel broadcasting
        if(sdStateReceiver!=null){
            unregisterReceiver(sdStateReceiver);
        }
        if(logTaskReceiver!=null){
            unregisterReceiver(logTaskReceiver);
        }
    }


    /**
     * This method is called once every time a service is opened for initialization.
     */
    @Override
    public void onCreate() {
        super.onCreate();
        init();
        register();
        deploySwitchLogFileTask();
        new LogCollectorThread().start();
    }

    private void init(){
        //The path of the log file in memory (the path of the log file in the installation directory)
        LOG_PATH_MEMORY_DIR = getFilesDir().getAbsolutePath() + File.separator + "log";
        //Log generated by this service to record the failure information of log service opening
        String LOG_SERVICE_LOG_PATH = LOG_PATH_MEMORY_DIR + File.separator + logServiceLogName;
        //The path of log files in sdcard
        LOG_PATH_SDCARD_DIR = FileUtils.getLocalRootSavePathDir("logger")+ File.separator + "log";

        //Create a log folder
        createLogDir();

        try {
            //true means that it can be written
            FileOutputStream fos = new FileOutputStream(LOG_SERVICE_LOG_PATH, true);
            writer = new OutputStreamWriter(fos);
        } catch (FileNotFoundException e) {
            Log.e(TAG, e.getMessage(), e);
        }

        //Get PowerManager Manager Manager
        PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
        if (pm != null) {
            wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        }

        //Current logging types
        CURR_LOG_TYPE = getCurrLogType();
        Log.i(TAG, "LogService onCreate");
    }


    private void register(){
        IntentFilter sdCarMonitorFilter = new IntentFilter();
        sdCarMonitorFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
        sdCarMonitorFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
        sdCarMonitorFilter.addDataScheme("file");
        sdStateReceiver = new SDStateMonitorReceiver();
        registerReceiver(sdStateReceiver, sdCarMonitorFilter);

        IntentFilter logTaskFilter = new IntentFilter();
        logTaskFilter.addAction(MONITOR_LOG_SIZE_ACTION);
        logTaskFilter.addAction(SWITCH_LOG_FILE_ACTION);
        logTaskReceiver = new LogTaskReceiver();
        registerReceiver(logTaskReceiver,logTaskFilter);
    }



    /**
     * Get whether the current storage should be in memory or SDCard
     * @return                  If there is an sd card, put it in the sd card; if not, put it in the memory card.
     */
    public int getCurrLogType(){
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            return MEMORY_TYPE;
        }else{
            return SDCARD_TYPE;
        }
    }

    /**
     * Deploy log switching tasks and switch log files every morning
     */
    private void deploySwitchLogFileTask() {
        Intent intent = new Intent(SWITCH_LOG_FILE_ACTION);
        PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, 0);
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);

        // Deployment task
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        if (am != null) {
            am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, sender);
        }
        recordLogServiceLog("deployNextTask success,next task time is:"+myLogSdf.format(calendar.getTime()));
    }

    /**
     * Log collection
     * 1.Clear Log Cache
     * 2.Kill the Logcat process that the application has opened to prevent multiple processes from writing to a log file
     * 3.Open the log collection process
     * 4.Processing log files, mobile OR deletion
     */
    class LogCollectorThread extends Thread {

        LogCollectorThread(){
            super("LogCollectorThread");
            Log.d(TAG, "LogCollectorThread is create");
        }

        @SuppressLint("WakelockTimeout")
        @Override
        public void run() {
            try {
                //Wake-up call
                wakeLock.acquire();
                //Clear the log cache before recording each log, otherwise duplicate logs will be recorded in two log files
                clearLogCache();
                //Running PS command to get process information
                List<String> orgProcessList = getAllProcess();
                //Get information such as PID, User, name according to the content of ps command
                List<ProcessInfo> processInfoList = getProcessInfoList(orgProcessList);
                //Close the logcat process opened by this program
                killLogcatPro(processInfoList);
                //Start collecting log information
                createLogCollector();
                //Hibernate, create a file, and then process the file. Otherwise, the file has not been created, which will affect file deletion.
                Thread.sleep(1000);
                //Processing log files, which mainly deal with sd card and memory card switching storage log logic
                handleLog();
                //release
                wakeLock.release();
            } catch (Exception e) {
                e.printStackTrace();
                recordLogServiceLog(Log.getStackTraceString(e));
            }
        }
    }

    /**
     * Clear the log cache before recording each log, otherwise duplicate logs will be recorded in two log files
     */
    private void clearLogCache() {
        Process pro = null;
        List<String> commandList = new ArrayList<>();
        commandList.add("logcat");
        commandList.add("-c");
        try {
            pro = Runtime.getRuntime().exec(commandList.toArray(new String[commandList.size()]));
            StreamConsumer errorGobbler = new StreamConsumer(pro.getErrorStream());
            StreamConsumer outputGobbler = new StreamConsumer(pro.getInputStream());
            errorGobbler.start();
            outputGobbler.start();
            if (pro.waitFor() != 0) {
                Log.e(TAG, " clearLogCache proc.waitFor() != 0");
                recordLogServiceLog("clearLogCache clearLogCache proc.waitFor() != 0");
            }
        } catch (Exception e) {
            Log.e(TAG, "clearLogCache failed", e);
            recordLogServiceLog("clearLogCache failed");
        } finally {
            try {
                if (pro != null) {
                    pro.destroy();
                }
            } catch (Exception e) {
                Log.e(TAG, "clearLogCache failed", e);
                recordLogServiceLog("clearLogCache failed");
            }
        }
    }

    /**
     * Close the logcat process opened by this program:
     * Kill the process according to the user name (if the Logcat collection process is opened by the process of this program, then the USER of both processes is the same)
     * If you do not close, there will be multiple processes reading logcat log caching information to write to the log file
     * @param allPro                        allPro
     */
    private void killLogcatPro(List<ProcessInfo> allPro) {
        if(process != null){
            process.destroy();
        }
        String packName = this.getPackageName();
        //Get the user name of the program
        String myUser = getAppUser(packName, allPro);

        for (ProcessInfo processInfo : allPro) {
            if (processInfo.name.toLowerCase().equals("logcat") && processInfo.user.equals(myUser)) {
                android.os.Process.killProcess(Integer.parseInt(processInfo.pid));
                //recordLogServiceLog("kill another logcat process success,the process info is:"
                //      + processInfo);
            }
        }
    }

    /**
     * Get the user name of the program
     * @param packName                              packName
     * @param allProList                            allProList
     * @return                                      Program name
     */
    private String getAppUser(String packName, List<ProcessInfo> allProList) {
        for (ProcessInfo processInfo : allProList) {
            if (processInfo.name.equals(packName)) {
                return processInfo.user;
            }
        }
        return null;
    }

    /**
     * Get information such as PID, User, name according to the content of ps command
     * @param orgProcessList                orgProcessList
     * @return                              aggregate
     */
    private List<ProcessInfo> getProcessInfoList(List<String> orgProcessList) {
        List<ProcessInfo> proInfoList = new ArrayList<>();
        for (int i = 1; i < orgProcessList.size(); i++) {
            String processInfo = orgProcessList.get(i);
            String[] proStr = processInfo.split(" ");
            // USER PID PPID VSIZE RSS WCHAN PC NAME
            // root 1 0 416 300 c00d4b28 0000cd5c S /init
            List<String> orgInfo = new ArrayList<>();
            for (String str : proStr) {
                if (!"".equals(str)) {
                    orgInfo.add(str);
                }
            }
            if (orgInfo.size() == 9) {
                ProcessInfo pInfo = new ProcessInfo();
                pInfo.user = orgInfo.get(0);
                pInfo.pid = orgInfo.get(1);
                pInfo.ppid = orgInfo.get(2);
                pInfo.name = orgInfo.get(8);
                proInfoList.add(pInfo);
            }
        }
        return proInfoList;
    }

    /**
     * Running PS command to get process information
     * @return
     *          USER PID PPID VSIZE RSS WCHAN PC NAME
     *          root 1 0 416 300 c00d4b28 0000cd5c S /init
     */
    private List<String> getAllProcess() {
        List<String> orgProList = new ArrayList<>();
        Process pro = null;
        try {
            pro = Runtime.getRuntime().exec("ps");
            StreamConsumer errorConsumer = new StreamConsumer(pro.getErrorStream());
            StreamConsumer outputConsumer = new StreamConsumer(pro.getInputStream(), orgProList);
            errorConsumer.start();
            outputConsumer.start();
            if (pro.waitFor() != 0) {
                Log.e(TAG, "getAllProcess pro.waitFor() != 0");
                recordLogServiceLog("getAllProcess pro.waitFor() != 0");
            }
        } catch (Exception e) {
            Log.e(TAG, "getAllProcess failed", e);
            recordLogServiceLog("getAllProcess failed");
        } finally {
            try {
                if (pro != null) {
                    pro.destroy();
                }
            } catch (Exception e) {
                Log.e(TAG, "getAllProcess failed", e);
                recordLogServiceLog("getAllProcess failed");
            }
        }
        return orgProList;
    }

    /**
     * Start collecting log information
     * Logs include, f, d, v, time,
     */
    public void createLogCollector() {
        // Log file name
        String logFileName = sdf.format(new Date()) + ".log";
        List<String> commandList = new ArrayList<>();
        commandList.add("logcat");
        commandList.add("-f");
        commandList.add("-d");
        //commandList.add(LOG_PATH_INSTALL_DIR + File.separator + logFileName);
        commandList.add(getLogPath());
        commandList.add("-v");
        commandList.add("time");

        // Filter all i information
        commandList.add("*:I");

        // Filter all error messages
        //commandList.add("*:E");

        // Filtering information for specified TAG
        // commandList.add("MyAPP:V");
        // commandList.add("*:S");
        try {
            process = Runtime.getRuntime().exec(commandList.toArray(new String[commandList.size()]));
            recordLogServiceLog("start collecting the log,and log name is:"+logFileName);
            // process.waitFor();
        } catch (Exception e) {
            Log.e(TAG, "CollectorThread == >" + e.getMessage(), e);
            recordLogServiceLog("CollectorThread == >" + e.getMessage());
        }
    }

    /**
     * Obtain the absolute storage path of the log based on the current storage location
     * @return                      Route
     */
    public String getLogPath(){
        createLogDir();
        // Log file name
        String logFileName = sdf.format(new Date()) + ".log";
        if(CURR_LOG_TYPE == MEMORY_TYPE){
            CURR_INSTALL_LOG_NAME = logFileName;
            Log.d(TAG, "Log stored in memory, the path is:"+LOG_PATH_MEMORY_DIR + File.separator + logFileName);
            return LOG_PATH_MEMORY_DIR + File.separator + logFileName;
        }else{
            CURR_INSTALL_LOG_NAME = null;
            Log.d(TAG, "Log stored in SDcard, the path is:"+LOG_PATH_SDCARD_DIR + File.separator + logFileName);
            return LOG_PATH_SDCARD_DIR + File.separator + logFileName;
        }
    }

    /**
     * Processing log files
     * 1.If the log file storage location is switched to memory, delete the log file except the one being written
     *   And deploy log size monitoring tasks to control log size not exceeding specified values
     * 2.If the location of the log file is switched to SDCard, delete the log 7 days ago and move it.
     *     Move all logs stored in memory to SDCard and size the previously deployed logs
     *   Monitoring cancellation
     */
    public void handleLog(){
        //The current logging type is stored in memory
        if(CURR_LOG_TYPE == MEMORY_TYPE){
            //Deploy log size monitoring tasks
            deployLogSizeMonitorTask();
            //Delete out-of-date logs in memory, delete rules: all but the current log and the most recent log saved are deleted
            deleteMemoryExpiredLog();
        }else{
            //Transfer log files to the SD card
            moveLogfile();
            //Cancel Deployment Log Size Monitoring Task
            cancelLogSizeMonitorTask();
            //Delete out-of-memory logs
            deleteSDCardExpiredLog();
        }
    }

    /**
     * Deploy log size monitoring tasks
     */
    private void deployLogSizeMonitorTask() {
        //If you are currently monitoring, you do not need to continue deploying
        if(logSizeMoniting){
            return;
        }
        logSizeMoniting = true;
        Intent intent = new Intent(MONITOR_LOG_SIZE_ACTION);
        PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, 0);
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        if (am != null) {
            am.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(), MEMORY_LOG_FILE_MONITOR_INTERVAL, sender);
        }
        Log.d(TAG, "deployLogSizeMonitorTask() suc !");
        //recordLogServiceLog("deployLogSizeMonitorTask() succ ,start time is " + calendar.getTime().toLocaleString());
    }

    /**
     * Cancel Deployment Log Size Monitoring Task
     */
    private void cancelLogSizeMonitorTask() {
        logSizeMoniting = false;
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent intent = new Intent(MONITOR_LOG_SIZE_ACTION);
        PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, 0);
        if (am != null) {
            am.cancel(sender);
        }
        Log.d(TAG, "canelLogSizeMonitorTask() suc");
    }

    /**
     * Check whether the log file size exceeds the specified size
     * If a log collection process has exceeded reopening
     */
    private void checkLogSize(){
        //When the file is not empty
        if(CURR_INSTALL_LOG_NAME != null && CURR_INSTALL_LOG_NAME.length()>0){
            //The path of the log file in memory + If the current log is written in memory, record the name of the current log file
            String path = LOG_PATH_MEMORY_DIR + File.separator + CURR_INSTALL_LOG_NAME;
            File file = new File(path);
            if(!file.exists()){
                return;
            }
            Log.d(TAG, "checkLog() ==> The size of the log is too big?");
            //When the file length >= 10M, create a new folder
            if(file.length() >= MEMORY_LOG_FILE_MAX_SIZE){
                Log.d(TAG, "The log's size is too big!");
                new LogCollectorThread().start();
            }
        }
    }

    /**
     * Create a log directory
     */
    private void createLogDir() {
        //A folder for the path of a log file in memory
        File file = new File(LOG_PATH_MEMORY_DIR);
        boolean mkOk;
        if (!file.isDirectory() && file.exists()) {
            mkOk = file.mkdirs();
            if (!mkOk) {
                //noinspection ResultOfMethodCallIgnored
                file.mkdirs();
            }
        }

        //Determine whether SD card exists and whether it has read and write permission
        //Create folders under SD card path
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            file = new File(LOG_PATH_SDCARD_DIR);
            if (!file.isDirectory()) {
                mkOk = file.mkdirs();
                if (!mkOk) {
                    recordLogServiceLog("move file failed,dir is not created succ");
                }
            }
        }
    }

    /**
     * Transfer log files to the SD card
     */
    private void moveLogfile() {
        //First, the state of sd card is judged
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            //recordLogServiceLog("move file failed, sd card does not mount");
            return;
        }
        File file = new File(LOG_PATH_SDCARD_DIR);
        if (!file.isDirectory()) {
            boolean mkOk = file.mkdirs();
            if (!mkOk) {
                //recordLogServiceLog("move file failed,dir is not created succ");
                return;
            }
        }

        file = new File(LOG_PATH_MEMORY_DIR);
        if (file.isDirectory()) {
            File[] allFiles = file.listFiles();
            for (File logFile : allFiles) {
                String fileName = logFile.getName();
                if (logServiceLogName.equals(fileName)) {
                    continue;
                }
                //String createDateInfo = getFileNameWithoutExtension(fileName);
                File beforeFile = new File(LOG_PATH_SDCARD_DIR + File.separator + fileName);
                boolean isSuccess = copy(logFile,beforeFile);
                if (isSuccess) {
                    //noinspection ResultOfMethodCallIgnored
                    logFile.delete();
                    //recordLogServiceLog("move file success,log name is:"+fileName);
                }
            }
        }
    }

    /**
     * Delete out-of-memory logs
     */
    private void deleteSDCardExpiredLog() {
        File file = new File(LOG_PATH_SDCARD_DIR);
        if (file.isDirectory()) {
            File[] allFiles = file.listFiles();
            for (File logFile : allFiles) {
                String fileName = logFile.getName();
                if (logServiceLogName.equals(fileName)) {
                    continue;
                }
                //Remove file extension types
                String createDateInfo = getFileNameWithoutExtension(fileName);
                //Determine whether log files on sdcard can be deleted
                if (canDeleteSDLog(createDateInfo)) {
                    //noinspection ResultOfMethodCallIgnored
                    logFile.delete();
                    Log.d(TAG, "delete expired log success,the log path is:" + logFile.getAbsolutePath());

                }
            }
        }
    }

    /**
     * Determine whether log files on sdcard can be deleted
     * @param createDateStr                     createDateStr
     * @return                                  Can I delete it?
     */
    public boolean canDeleteSDLog(String createDateStr) {
        boolean canDel ;
        //Get the current time
        Calendar calendar = Calendar.getInstance();
        //Delete logs 7 days ago
        calendar.add(Calendar.DAY_OF_MONTH, -1 * SDCARD_LOG_FILE_SAVE_DAYS);
        Date expiredDate = calendar.getTime();
        try {
            Date createDate = sdf.parse(createDateStr);
            canDel = createDate.before(expiredDate);
        } catch (ParseException e) {
            Log.e(TAG, e.getMessage(), e);
            canDel = false;
        }
        return canDel;
    }


    /**
     * Delete expired logs in memory and delete rules:
     * All but the current log and the most recent log saved are deleted.
     */
    private void deleteMemoryExpiredLog(){
        File file = new File(LOG_PATH_MEMORY_DIR);
        if (file.isDirectory()) {
            File[] allFiles = file.listFiles();
            Arrays.sort(allFiles, new FileComparator());
            for (int i=0;i<allFiles.length-2;i++) {  //"-2" saves the last two log files
                File _file =  allFiles[i];
                if (logServiceLogName.equals(_file.getName()) ||  _file.getName().equals(CURR_INSTALL_LOG_NAME)) {
                    continue;
                }
                //noinspection ResultOfMethodCallIgnored
                _file.delete();
                Log.d(TAG, "delete expired log success,the log path is:"+_file.getAbsolutePath());
            }
        }
    }

    /**
     * Copy file
     * @param source                    file
     * @param target                    file
     * @return                          Is the copy successful?
     */
    private boolean copy(File source, File target) {
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            if(!target.exists()){
                boolean createSuccess = target.createNewFile();
                if(!createSuccess){
                    return false;
                }
            }
            in = new FileInputStream(source);
            out = new FileOutputStream(target);
            byte[] buffer = new byte[8*1024];
            int count;
            while ((count = in.read(buffer)) != -1) {
                out.write(buffer, 0, count);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, e.getMessage(), e);
            recordLogServiceLog("copy file fail");
            return false;
        } finally{
            try {
                if(in != null){
                    in.close();
                }
                if(out != null){
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage(), e);
                recordLogServiceLog("copy file fail");
            }
        }
    }



    /**
     * This note is a log of errors.
     * Recording the basic information of the log service prevents errors in the log service and cannot be found in the LogCat log
     * The log name is Log.log
     * @param msg                           msg
     */
    private void recordLogServiceLog(String msg) {
        if (writer != null) {
            try {
                Date time = new Date();
                writer.write(myLogSdf.format(time) + " : " + msg);
                writer.write("\n");
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage(), e);
            }
        }
    }

    /**
     * Remove file extension type (. log)
     * @param fileName                          fileName
     * @return                                  Character string
     */
    private String getFileNameWithoutExtension(String fileName){
        return fileName.substring(0, fileName.indexOf("."));
    }

    class ProcessInfo {
        public String user;
        private String pid;
        private String ppid;
        public String name;

        @Override
        public String toString() {
            return "ProcessInfo{" +
                    "user='" + user + '\'' +
                    ", pid='" + pid + '\'' +
                    ", ppid='" + ppid + '\'' +
                    ", name='" + name + '\'' +
                    '}';
        }
    }


    class StreamConsumer extends Thread {
        InputStream is;
        List<String> list;

        StreamConsumer(InputStream is) {
            this.is = is;
        }

        StreamConsumer(InputStream is, List<String> list) {
            this.is = is;
            this.list = list;
        }

        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line ;
                while ((line = br.readLine()) != null) {
                    if (list != null) {
                        list.add(line);
                    }
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }

    /**
     * Monitoring SD Card Status
     * @author Administrator
     *
     */
    private class SDStateMonitorReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            //Storage Card Unloaded
            if(Intent.ACTION_MEDIA_UNMOUNTED.equals(intent.getAction())){
                if(CURR_LOG_TYPE == SDCARD_TYPE){
                    Log.d(TAG, "SDCard is UNMOUNTED");
                    CURR_LOG_TYPE = MEMORY_TYPE;
                    new LogCollectorThread().start();
                }
            }else{
                //Storage card mounted
                if(CURR_LOG_TYPE == MEMORY_TYPE){
                    Log.d(TAG, "SDCard is MOUNTED");
                    CURR_LOG_TYPE = SDCARD_TYPE;
                    new LogCollectorThread().start();
                }
            }
        }
    }

    /**
     * Log Task Receiving Broadcast
     * Switch logs to monitor log size
     * @author Administrator
     *
     */
    class LogTaskReceiver extends BroadcastReceiver{
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            //Switching log file action
            if(SWITCH_LOG_FILE_ACTION.equals(action)){
                new LogCollectorThread().start();
            }else if(MONITOR_LOG_SIZE_ACTION.equals(action)){
                //Log file monitoring action
                checkLogSize();
            }
        }
    }


    class FileComparator implements Comparator<File> {
        public int compare(File file1, File file2) {
            if(logServiceLogName.equals(file1.getName())){
                return -1;
            }else if(logServiceLogName.equals(file2.getName())){
                return 1;
            }

            String createInfo1 = getFileNameWithoutExtension(file1.getName());
            String createInfo2 = getFileNameWithoutExtension(file2.getName());

            try {
                Date create1 = sdf.parse(createInfo1);
                Date create2 = sdf.parse(createInfo2);
                if(create1.before(create2)){
                    return -1;
                }else{
                    return 1;
                }
            } catch (ParseException e) {
                return 0;
            }
        }
    }

}

Posted by ok on Wed, 15 May 2019 05:24:52 -0700