欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

保存Android日志到文件

程序员文章站 2022-03-09 16:16:55
...

前言

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

PS命令

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

保存Android日志到文件

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

保存Android日志到文件

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.
 * e-mail:aaa@qq.com
 * 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();
            }
        }
    }

}
相关标签: android logcat