保存Android日志到文件

文章目录


##前言
之前写过一篇日志, 《不是安卓工程师,也能看logcat日志》,不仅需要adb环境还要熟悉logcat命令,感觉比较繁琐。现推出升级版,在Service中保存日志服务。同样用到命令 logcat -f logpath。在这之前,已有人造过同样的轮子。只是代码更长,功能更完善。比如:监听日志文件大小,超出10M就重新生成日志文件。个人认为,这些功能有些画蛇添足。试问,有谁会从10M日志文件中去找调试信息。so,出一个简化版,方便阅读、方便理解。

##PS命令
此PS,非PhotoShop。熟悉linux 都知道,ps 命令可以list出系统中所有正在运行的线程。

这里写图片描述

图示是在adb shell中,list出当前连接设备正在运行的所有进程信息。

这里写图片描述

grep参数做过滤操作,之前在查看指定pid的日志中有提过。(Android 7.0 开始logcat 支持pid 筛选,如:logcat --pid=进程id)。从上图中,可以看出:

进程A 创建进程B
A.USER == B.USER
A.PID == B.PPID

同样,进程A创建的logcat进程只能保存进程A 的全部日志,执行的PS命令只能list出同USER属性相同的进程。

##保存日志

    /**
     * 开始保存日志
     */
    private void startSaveLog() {
        try {
            Runtime.getRuntime().exec("logcat -f " + logFilePath);
            //获取新生成的logcat 进程信息
            logcatInfo = getLogcatProInfo(getProcessInfoStr("logcat"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

##清除日志缓存

    /**
     * 清除日志缓存
     */
    private void clearLogCache() {
        try {
            //清除日志缓存
            Runtime.getRuntime().exec("logcat -c");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

在开始保存日志前调用,清除之前已经输出的日志。

##关闭进程

    /**
     * 删除logcat进程,退出保存日志
     */
    private void killLogcat() {
        if (logcatInfo == null)
            return;

        try {
            Runtime.getRuntime().exec("kill " + logcatInfo.pid);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

可以在任意位置发送广播,停止保存日志。

##广播控制日志保存和关闭

 /**
     * 保存日志操作广播
     */
    class SaveLogReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(KILL_LOGCAT_ACTION)) {
                killLogcat();
            } else if (intent.getAction().equals(START_LOGCAT_ACTION)) {
                clearLogCache();
                startSaveLog();
            }
        }
    }

同样,可以在任意地方重开始,继续保存日志。这样可以有效避免保存了无用的日志信息,使日志内容又臭又长。

##获取当前应用的进程信息

 appProInfo = new ProcessInfo(getProcessInfoStr(getPackageName()).get(0));
 
	/**
     * 根据名称获取线程信息
     *
     * @param name
     * @return 返回线程信息的字符串格式
     */
    private ArrayList<String> getProcessInfoStr(String name) {

        ArrayList<String> strs = new ArrayList<String>();
        try {
            java.lang.Process process = Runtime.getRuntime().exec("ps");
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line.endsWith(name)) {
                    strs.add(line);
                }
            }
            br.close();
            isr.close();
            is.close();

            br = null;
            isr = null;
            is = null;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return strs;
    }

	/**
     * 进程信息,PS命令获取
     */
    class ProcessInfo {
        private String user;
        private String pid;
        private String ppid;
        private String vsize;
        private String rss;
        private String wchan;
        private String pc;
        private String name;

        public ProcessInfo(String info) {
            if (info == null)
                return;
            //info 可能连续包含多个空格,因此用split方法无效。
            ArrayList<String> item = new ArrayList<String>();
            for (int i = 0; i < info.length(); i++) {
                if (info.charAt(i) == ' ') {
                    continue;
                }
                int j = i + 1;
                while (j < info.length() && info.charAt(j++) != ' ') ;
                item.add(info.substring(i, j - 1));
                i = j - 1;
            }

            user = item.get(0);
            pid = item.get(1);
            ppid = item.get(2);
            vsize = item.get(3);
            rss = item.get(4);
            wchan = item.get(5);
            pc = item.get(6);
            name = item.get(8);
        }
 
    }

##使用方法
1、在AndroidManife.xml里注册Service
2、启动Service,开始保存日志
3、发送广播,停止保存日志
4、发送广播,继续保存日志

最后附上源码:


/**
 * 保存日志服务
 * Created by 左克飞 on 17/8/3 下午3:25.
 * FilePath App:com.flueky.framework.android
 */

public class SaveLogService extends Service {

    private String logFoldPath;//日志文件夹路径
    private String logFilePath;//日志文件路径
    public static final String KILL_LOGCAT_ACTION = "com.flueky.action.kill_logcat";
    public static final String START_LOGCAT_ACTION = "com.flueky.action.start_logcat";

    private ProcessInfo appProInfo;//应用进程信息
    private ProcessInfo logcatInfo;//日志进程信息
    private SaveLogReceiver saveLogReceiver;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        logFoldPath = getExternalCacheDir().getAbsolutePath() + "/../log";

        saveLogReceiver = new SaveLogReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(KILL_LOGCAT_ACTION);
        filter.addAction(START_LOGCAT_ACTION);
        registerReceiver(saveLogReceiver, filter);
    }


    /**
     * 校验日志目录文件夹
     *
     * @return
     */
    private boolean checkLogFolder() {
        File file = new File(logFoldPath);
        if (!file.exists()) {
            return file.mkdirs();
        }
        return true;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        appProInfo = new ProcessInfo(getProcessInfoStr(getPackageName()).get(0));
        //清除日志缓存
        clearLogCache();
        //开始保存日志
        startSaveLog();
        return super.onStartCommand(intent, flags, startId);

    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(saveLogReceiver);
    }

    /**
     * 根据名称获取线程信息
     *
     * @param name
     * @return 返回线程信息的字符串格式
     */
    private ArrayList<String> getProcessInfoStr(String name) {

        ArrayList<String> strs = new ArrayList<String>();
        try {
            java.lang.Process process = Runtime.getRuntime().exec("ps");
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line.endsWith(name)) {
                    strs.add(line);
                }
            }
            br.close();
            isr.close();
            is.close();

            br = null;
            isr = null;
            is = null;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return strs;
    }


    /**
     * 获取保存日志的进程信息
     *
     * @param logcatProcessInfos
     * @return
     */
    private ProcessInfo getLogcatProInfo(ArrayList<String> logcatProcessInfos) {
        if (logcatProcessInfos == null)
            return null;
        for (String infoStr : logcatProcessInfos) {
            ProcessInfo info = new ProcessInfo(infoStr);
            if (info.user.equals(appProInfo.user)) {
                return info;
            }
        }
        return null;
    }

    /**
     * 删除logcat进程,退出保存日志
     */
    private void killLogcat() {
        if (logcatInfo == null)
            return;

        try {
            Runtime.getRuntime().exec("kill " + logcatInfo.pid);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 清除日志缓存
     */
    private void clearLogCache() {
        try {
            //清除日志缓存
            Runtime.getRuntime().exec("logcat -c");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始保存日志
     */
    private void startSaveLog() {
        try {
	        if (checkLogFolder())
	            logFilePath = logFoldPath + "/HH-" + new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime()) + ".log";
            Runtime.getRuntime().exec("logcat -f " + logFilePath);
            logcatInfo = getLogcatProInfo(getProcessInfoStr("logcat"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 进程信息,PS命令获取
     */
    class ProcessInfo {
        private String user;
        private String pid;
        private String ppid;
        private String vsize;
        private String rss;
        private String wchan;
        private String pc;
        private String name;

        public ProcessInfo(String info) {
            if (info == null)
                return;
            ArrayList<String> item = new ArrayList<String>();
            for (int i = 0; i < info.length(); i++) {
                if (info.charAt(i) == ' ') {
                    continue;
                }
                int j = i + 1;
                while (j < info.length() && info.charAt(j++) != ' ') ;
                item.add(info.substring(i, j - 1));
                i = j - 1;
            }

            user = item.get(0);
            pid = item.get(1);
            ppid = item.get(2);
            vsize = item.get(3);
            rss = item.get(4);
            wchan = item.get(5);
            pc = item.get(6);
            name = item.get(8);
        }

        @Override
        public String toString() {
            StringBuffer buffer = new StringBuffer();
            buffer.append(user).
                    append(" ").
                    append(pid).
                    append(" ").
                    append(ppid).
                    append(" ").
                    append(vsize).
                    append(" ").
                    append(rss).
                    append(" ").
                    append(wchan).
                    append(" ").
                    append(pc).
                    append(" ").
                    append(name);

            return buffer.toString();
        }
    }

    /**
     * 保存日志操作广播
     */
    class SaveLogReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(KILL_LOGCAT_ACTION)) {
                killLogcat();
            } else if (intent.getAction().equals(START_LOGCAT_ACTION)) {
                clearLogCache();
                startSaveLog();
            }
        }
    }

}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值