目录介绍

  • 1.业务需求
  • 2.目前有那些做法及问题
  • 3.自定义log工具类,一边打印日志一边写入文件
  • 4.在application开启线程池进行日志打印
  • 5.写一个service将系统日志写到文件

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

关于链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.程序员聊天笔记汇总
  • 5.其他汇总

1.业务需求

  • 要求将app的系统日志都写入到指定的文件目录下的文件中
  • 要求写入文件达到一定大小后自动切换到下一个文件夹
  • 要求app在运行时,持续将日志写到文件中
  • 要求可以清除7天或者n天之前的日志文件
  • 要求有sd卡,写日志到sd卡文件;没有则写到内存卡;如果从无sd卡到有sd卡,那么可以切换路径
  • 关于代码地址可以参考:https://github.com/yangchong211/YCAudioPlayer

2.目前有那些做法及问题

  • 1.自定义log工具类,在输出日志的时候,可以将输出日志写到文件夹。不过只能记录priority5中状态日志
  • 2.在application开启线程池,然后调用PrintToFileUtil类(自己写的工具类)中setPrint方法。存在问题:app一直开启状态下,无法持续打印,还未找到原因
  • 3.开启app时,打开service服务,当app进程不杀死时,或者service不销毁时,会一直运行在后台,那么也可以处理打印日志逻辑。并且通过广播可以监听sd状态来达到切换日志路径的目的

3.自定义log工具类,一边打印日志一边写入文件

  • 3.1 直接调用代码如下所示:
public static void e(String msg) {if (isDebug){Log.e(TAG, msg);PrintToFileUtil.input2File(msg,"path");}
}
  • 3.2 工具类如下所示
/*** 将内容直接写过文件中,自己设置路径* 这个是一边打印日志,一边将日志写入到file* 不建议直接new一个子线程做写入逻辑,建议开启线程池,避免到处开启线程损耗性能* @param input                     写入内容* @param filePath                  路径* @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>() {@Overridepublic Boolean call() throws Exception {BufferedWriter bw = null;try {// 构造给定文件名的FileWriter对象,并使用布尔值指示是否追加写入的数据。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.在application开启线程池进行日志打印

/*** 遇到问题:* 1.什么时候调用这个方法?* 2.为什么调用这个方法会造成程序无响应,卡在什么地方?* 3.如何避免造成线程堵塞* 4.当打印到一定日志后,如何切换到下一个.txt文件?* @param logPath*/
public static void setPrint(String logPath){if (sExecutor == null) {sExecutor = Executors.newScheduledThreadPool(5);}//以xx月xx日为命名文件名,这样每天的文件都是不一样的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>() {@Overridepublic Boolean call() throws Exception {/*核心代码*/while(captureLogThreadOpen){/*命令的准备*/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{//抓取当前的缓存日志Process process = Runtime.getRuntime().exec(getLog.toArray(new String[getLog.size()]));//获取输入流BufferedReader buffRead = new BufferedReader(new InputStreamReader(process.getInputStream()));//清除是为了下次抓取不会从头抓取Runtime.getRuntime().exec(clearLog.toArray(new String[clearLog.size()]));String str = null;//打开文件File logFile = new File(newLogPath + "log.txt");//true表示在写的时候在文件末尾追加FileOutputStream fos = new FileOutputStream(logFile, true);//换行的字符串String newline = System.getProperty("line.separator");//Date date = new Date(System.currentTimeMillis());//String time = format.format(date);//Log.i(TAG, "thread");//打印设备信息fos.write(printDeviceInfo().getBytes());while((str=buffRead.readLine())!=null){         //循环读取每一行//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);//加上年fos.write((time + str).getBytes());//换行fos.write(newline.getBytes());logCount++;//大于100000行就退出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.写一个service将系统日志写到文件

  • 5.1 文档要点

    • 1.支持自定义设置日志写入的路径和文件名称;
    • 2.如果有sd卡,那么就记录在sd卡中;否则记录到内存卡中;
    • 3.如果之前日志记录在内存卡中,后期有sd卡,那么会将之前内存中的文件拷贝到SDCard中
    • 4.可以监听sd卡装载和卸载广播,从而切换保存日志的保存路径
    • 5.sd卡中可以设置日志保存的最长周期,目前设置为7天
    • 6.当写入日志的文件大于自定义最大值(目前是10M),可以自动切换到下一个文件夹
    • 7.支持清除日志缓存,每次记录日志之前先清除日志的缓存, 不然会在两个日志文件中记录重复的日志
    • 8.收集的日志包括:d,v,e,w,c
    • 9.支持超出最长保存日期后自动删除文件逻辑,删除逻辑是:取以日期命名的文件名称与当前时间比较
    • 10.app开启的时候打开该服务service,那么service在运行的时候会一直记录日志并且写到文件,除非杀死服务
    • 11.建议日志命名按照日期命名,方便处理删除逻辑
  • 5.2 具体的实现代码

    • 5.2.1 在清单文件注册
     <service android:name=".service.LogService" /><!--系统日志权限-->
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    • 5.2.2 关于logService代码如下
public class LogService extends Service {//日志private static final String TAG = "LogService";//内存中日志文件最大值,10Mprivate static final int MEMORY_LOG_FILE_MAX_SIZE = 10 * 1024 * 1024;//内存中的日志文件大小监控时间间隔,10分钟private static final int MEMORY_LOG_FILE_MONITOR_INTERVAL = 10 * 60 * 1000;//sd卡中日志文件的最多保存天数private static final int SDCARD_LOG_FILE_SAVE_DAYS = 7;//日志文件在内存中的路径(日志文件在安装目录中的路径)private String LOG_PATH_MEMORY_DIR;//日志文件在sdcard中的路径【主要这个很重要】private String LOG_PATH_SDCARD_DIR;//当前的日志记录类型为存储在SD卡下面private final int SDCARD_TYPE = 0;//当前的日志记录类型为存储在内存中private final int MEMORY_TYPE = 1;//当前的日志记录类型,默认是SD卡private int CURR_LOG_TYPE = SDCARD_TYPE;//如果当前的日志写在内存中,记录当前的日志文件名称private String CURR_INSTALL_LOG_NAME;//本服务输出的日志文件名称,也可以是txt格式,或者后缀名是.logprivate String logServiceLogName = "Log.log";@SuppressLint("SimpleDateFormat")private SimpleDateFormat myLogSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日志名称格式@SuppressLint("SimpleDateFormat")private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH时mm分ss秒");private OutputStreamWriter writer ;private Process process;//唤醒private PowerManager.WakeLock wakeLock;//SDCard状态监测,广播监听sd卡动态,比如用户拔下sd卡private SDStateMonitorReceiver sdStateReceiver;private LogTaskReceiver logTaskReceiver;/* 是否正在监测日志文件大小;* 如果当前日志记录在SDard中则为false* 如果当前日志记录在内存中则为true*/private boolean logSizeMoniting = false;//日志文件监测actionprivate static String MONITOR_LOG_SIZE_ACTION = "MONITOR_LOG_SIZE";//切换日志文件actionprivate static String SWITCH_LOG_FILE_ACTION = "SWITCH_LOG_FILE_ACTION";/*** 无需绑定* @param intent            intent* @return                  IBinder对象*/@Overridepublic IBinder onBind(Intent intent) {return null;}/*** 销毁时调用该方法*/@Overridepublic 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();}//注意需要注销广播if(sdStateReceiver!=null){unregisterReceiver(sdStateReceiver);}if(logTaskReceiver!=null){unregisterReceiver(logTaskReceiver);}}/*** 每次开启service时,都会调用一次该方法,用于初始化*/@Overridepublic void onCreate() {super.onCreate();init();register();deploySwitchLogFileTask();new LogCollectorThread().start();}private void init(){//日志文件在内存中的路径(日志文件在安装目录中的路径)LOG_PATH_MEMORY_DIR = getFilesDir().getAbsolutePath() + File.separator + "log";//本服务产生的日志,记录日志服务开启失败信息String LOG_SERVICE_LOG_PATH = LOG_PATH_MEMORY_DIR + File.separator + logServiceLogName;//日志文件在sdcard中的路径LOG_PATH_SDCARD_DIR = FileUtils.getLocalRootSavePathDir("logger")+ File.separator + "log";//创建log文件夹createLogDir();try {//true 表示可以接着写入FileOutputStream fos = new FileOutputStream(LOG_SERVICE_LOG_PATH, true);writer = new OutputStreamWriter(fos);} catch (FileNotFoundException e) {Log.e(TAG, e.getMessage(), e);}//获取PowerManager管理者PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);if (pm != null) {wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);}//当前的日志记录类型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);}/*** 获取当前应存储在内存中还是存储在SDCard中* @return                  如果有sd卡,就放到sd卡中;没有则放到内存卡中*/public int getCurrLogType(){if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {return MEMORY_TYPE;}else{return SDCARD_TYPE;}}/*** 部署日志切换任务,每天凌晨切换日志文件*/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);// 部署任务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()));}/*** 日志收集* 1.清除日志缓存* 2.杀死应用程序已开启的Logcat进程防止多个进程写入一个日志文件* 3.开启日志收集进程* 4.处理日志文件,移动 OR 删除*/class LogCollectorThread extends Thread {LogCollectorThread(){super("LogCollectorThread");Log.d(TAG, "LogCollectorThread is create");}@SuppressLint("WakelockTimeout")@Overridepublic void run() {try {//唤醒手机wakeLock.acquire();//每次记录日志之前先清除日志的缓存, 不然会在两个日志文件中记录重复的日志clearLogCache();//运行PS命令得到进程信息List<String> orgProcessList = getAllProcess();//根据ps命令得到的内容获取PID,User,name等信息List<ProcessInfo> processInfoList = getProcessInfoList(orgProcessList);//关闭由本程序开启的logcat进程killLogcatPro(processInfoList);//开始收集日志信息createLogCollector();//休眠,创建文件,然后处理文件,不然该文件还没创建,会影响文件删除Thread.sleep(1000);//处理日志文件,这里面主要是处理sd卡和内存卡切换存储日志逻辑handleLog();//释放wakeLock.release();} catch (Exception e) {e.printStackTrace();recordLogServiceLog(Log.getStackTraceString(e));}}}/*** 每次记录日志之前先清除日志的缓存, 不然会在两个日志文件中记录重复的日志*/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");}}}/*** 关闭由本程序开启的logcat进程:* 根据用户名称杀死进程(如果是本程序进程开启的Logcat收集进程那么两者的USER一致)* 如果不关闭会有多个进程读取logcat日志缓存信息写入日志文件* @param allPro                        allPro*/private void killLogcatPro(List<ProcessInfo> allPro) {if(process != null){process.destroy();}String packName = this.getPackageName();//获取本程序的用户名称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);}}}/*** 获取本程序的用户名称* @param packName                              packName* @param allProList                            allProList* @return                                      程序名称*/private String getAppUser(String packName, List<ProcessInfo> allProList) {for (ProcessInfo processInfo : allProList) {if (processInfo.name.equals(packName)) {return processInfo.user;}}return null;}/*** 根据ps命令得到的内容获取PID,User,name等信息* @param orgProcessList                orgProcessList* @return                              集合*/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 /initList<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;}/*** 运行PS命令得到进程信息* @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;}/*** 开始收集日志信息* 日志包括,f,d,v,time,*/public void createLogCollector() {// 日志文件名称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");// 过滤所有的i信息commandList.add("*:I");// 过滤所有的错误信息//commandList.add("*:E");// 过滤指定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());}}/*** 根据当前的存储位置得到日志的绝对存储路径* @return                      路径*/public String getLogPath(){createLogDir();// 日志文件名称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;}}/*** 处理日志文件* 1.如果日志文件存储位置切换到内存中,删除除了正在写的日志文件*   并且部署日志大小监控任务,控制日志大小不超过规定值* 2.如果日志文件存储位置切换到SDCard中,删除7天之前的日志,移*     动所有存储在内存中的日志到SDCard中,并将之前部署的日志大小*   监控取消*/public void handleLog(){//当前的日志记录类型为存储在内存中if(CURR_LOG_TYPE == MEMORY_TYPE){//部署日志大小监控任务deployLogSizeMonitorTask();//删除内存中的过期日志,删除规则:除了当前的日志和离当前时间最近的日志保存其他的都删除deleteMemoryExpiredLog();}else{//将日志文件转移到SD卡下面moveLogfile();//取消部署日志大小监控任务cancelLogSizeMonitorTask();//删除内存下过期的日志deleteSDCardExpiredLog();}}/*** 部署日志大小监控任务*/private void deployLogSizeMonitorTask() {//如果当前正在监控着,则不需要继续部署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());}/*** 取消部署日志大小监控任务*/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");}/*** 检查日志文件大小是否超过了规定大小* 如果超过了重新开启一个日志收集进程*/private void checkLogSize(){//当文件不为空时if(CURR_INSTALL_LOG_NAME != null && CURR_INSTALL_LOG_NAME.length()>0){//日志文件在内存中的路径+如果当前的日志写在内存中,记录当前的日志文件名称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?");//当文件长度>=10M时,重新创建一个新的文件夹if(file.length() >= MEMORY_LOG_FILE_MAX_SIZE){Log.d(TAG, "The log's size is too big!");new LogCollectorThread().start();}}}/*** 创建日志目录*/private void createLogDir() {//日志文件在内存中的路径的文件夹File file = new File(LOG_PATH_MEMORY_DIR);boolean mkOk;if (!file.isDirectory() && file.exists()) {mkOk = file.mkdirs();if (!mkOk) {//noinspection ResultOfMethodCallIgnoredfile.mkdirs();}}//判断SD卡是否存在,并且是否具有读写权限//创建SD卡路径下的文件夹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");}}}}/*** 将日志文件转移到SD卡下面*/private void moveLogfile() {//首先判断sd卡状态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 ResultOfMethodCallIgnoredlogFile.delete();//recordLogServiceLog("move file success,log name is:"+fileName);}}}}/*** 删除内存下过期的日志*/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;}//去除文件的扩展类型String createDateInfo = getFileNameWithoutExtension(fileName);//判断sdcard上的日志文件是否可以删除if (canDeleteSDLog(createDateInfo)) {//noinspection ResultOfMethodCallIgnoredlogFile.delete();Log.d(TAG, "delete expired log success,the log path is:" + logFile.getAbsolutePath());}}}}/*** 判断sdcard上的日志文件是否可以删除* @param createDateStr                     createDateStr* @return                                  是否可以删除*/public boolean canDeleteSDLog(String createDateStr) {boolean canDel ;//获取当前时间Calendar calendar = Calendar.getInstance();//删除7天之前日志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;}/*** 删除内存中的过期日志,删除规则:* 除了当前的日志和离当前时间最近的日志保存其他的都删除*/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"保存最近的两个日志文件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());}}}/*** 拷贝文件* @param source                    file* @param target                    file* @return                          是否拷贝成功*/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");}}}/*** 这个注意是记录错误的日志* 记录日志服务的基本信息 防止日志服务有错,在LogCat日志中无法查找* 此日志名称为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);}}}/*** 去除文件的扩展类型(.log)* @param fileName                          fileName* @return                                  字符串*/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;@Overridepublic 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();}}}/*** 监控SD卡状态* @author Administrator**/private class SDStateMonitorReceiver extends BroadcastReceiver {public void onReceive(Context context, Intent intent) {//存储卡被卸载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{//存储卡被挂载if(CURR_LOG_TYPE == MEMORY_TYPE){Log.d(TAG, "SDCard is MOUNTED");CURR_LOG_TYPE = SDCARD_TYPE;new LogCollectorThread().start();}}}}/*** 日志任务接收广播* 切换日志,监控日志大小* @author Administrator**/class LogTaskReceiver extends BroadcastReceiver{public void onReceive(Context context, Intent intent) {String action = intent.getAction();//切换日志文件actionif(SWITCH_LOG_FILE_ACTION.equals(action)){new LogCollectorThread().start();}else if(MONITOR_LOG_SIZE_ACTION.equals(action)){//日志文件监测actioncheckLogSize();}}}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;}}}}

Android 系统日志写入文件相关推荐

  1. android写入文件方法,Android 追加写入文件的三种方法

    一.使用FileOutputStream 使用FileOutputStream,在构造FileOutputStream时,把第二个参数设为true public static void method1 ...

  2. android文件的读取方法,Android读取写入文件的方法

    参考地址:https://blog.csdn.net/harry_helei/article/details/64910713 1.按行读取指定文件 /** * 按行读取指定文件,输出到ArrayLi ...

  3. Android 追加写入文件的三种方法

    一.使用FileOutputStream 使用FileOutputStream,在构造FileOutputStream时,把第二个参数设为true public static void method1 ...

  4. android hashmap 写入文件,android – ACRA 4.9.0:如何将ACRA报告写入文件(在应用程序数据文件夹中)...

    我想使用最新的Acra 4.9.0在文本文件中编写崩溃报告. 我可以举例说明这个最新版本. 我尝试使用可用的文档. Acra已启用 但它,不是写在文件中. myApp package com.myAp ...

  5. android studio 写文件,在Android Studio中从我自己的类读取和写入文本文件

    我一直在试图创建一个名为TextFileReaderWriter的类我想使用getters和setters来读取和写入文本文件,以便我可以调用类,并通过简单地使用setfileContents(som ...

  6. android 追加写入数据到文件

    今天,简单讲讲android如何以追加的形式写入数据到文件里. 这个其实也比较简单,不过之前一直没有使用,所以昨天查询了资料,这里记录一下. import java.io.BufferedWriter ...

  7. android逐行写入读取_Android外部存储-读取,写入,保存文件

    android逐行写入读取 Android external storage can be used to write and save data, read configuration files ...

  8. 关于Android写入文件失败的问题

    关于Android写入文件失败的问题 由于本人在Android开发的时候,经常性的写入文件,每次都写入异常,导致写入文件失败,经过慢慢的研究和反复的测试,总结出写入问题的失败的原因(比较菜,写的不好勿 ...

  9. 以Android L读取系统所有logcat并写入文件为例分析Android 以添加系统进程的方式申请selinux的权限执行shell脚本,以及avc:dined应该怎么申请权限

    添加selinux较好的文章,可以通过adb shell dmesg > kenel.log 获取kmesg 可以看到 avc:dined 相关内容 首先说说环境: 基于Android L的ao ...

最新文章

  1. 健忘族的大爱,机器人管家Watch Bot提醒你不要丢三落四
  2. 安卓USB开发教程 二 USB Host
  3. (vue基础试炼_04)使用组件改造TodoList
  4. 呆错网址导航系统V1.13.18 PHP源码
  5. centos7 关闭防火墙的指令_Centos7.3防火墙配置
  6. SpringSecurity框架【详解】
  7. Python学习之路day01——002简单数据类型
  8. Windows 10 安装(硬盘装机)
  9. Python植物大战僵尸源代码及素材
  10. 拜占庭将军们的投票出了问题
  11. 适用于Win7系统下Intel 7代核心显卡驱动程序
  12. 【毕业设计】python opencv 深度学习 指纹识别算法实现
  13. 多媒体大作业 ps+flash
  14. android apk很大,从Android Studio生成的Apk文件太大
  15. Android Camera基本架构介绍
  16. 微信连WiFi关注公众号流程更新 解决ios微信扫描二维码不关注就能上网的问题
  17. 安装mysq的时候执行mysqld --initialize --console时候报错:can not create directory data文件夹需要把分隔符改成反方向
  18. 扎克伯格“致敬”微信,但Facebook Pay能像微信支付一样成功吗?
  19. ATF:Gicv源码文件系列-gicv2.h
  20. springboot生成条形码并直接输出显示

热门文章

  1. 洛谷 P1010 [NOIP1998 普及组] 幂次方 Python题解
  2. maya python教程下载_[转载]技术教程-MayaPython教程四之实战篇
  3. 多值依赖与部分函数依赖 转
  4. 谢少荣到计算机学院,我院获批国家自然科学基金委人工智能代码(F06)首个重大项目...
  5. Java 8 实战教学大纲
  6. linux 查找替换
  7. python调用谷歌翻译Googletrans接口
  8. Android S 新特性:应用行为更改
  9. 嵌入式工控机在舞台灯光控制中的应用
  10. 不用PLC,怎么用组态王和step7编程软件结合实现新年晚会舞台灯光效果,简单好看怎么做?