android: 记录app运行过程中产生的log

摘要:
有时,在解决问题时,我们经常需要使用logcat来分析和定位问题。这里有一个小工具,可以在应用程序运行期间记录日志。这样,测试人员只需要在反馈bug时向我们发送logcat。

有时在解决问题时,经常需要借助logcat才能分析定位问题,这里写了一个小工具,能够记录app运行期间的log, 这样测试人员在反馈bug时,只需要把logcat发给我们就可以了。具体代码如下:

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * A tool class is used to capture the logcat generated by the app running.
 * <p>The logcat save path: sdcard/Android/data/packageName/files/Documents/logcatSaveDir</p>
 *
 * @author xp.chen
 */
public class LogWatcher {

    public static final String TAG = LogWatcher.class.getSimpleName();

    private static volatile LogWatcher instance = null;

    private static final String LOG_FILE_PREFIX = "logcat_";
    private static final String LOG_FILE_SUFFIX = ".txt";

    private static String sLogDirPath;

    private Context mContext;

    private Process mLogcatProcess;

    private File mLogcatFile;

    private LogWatcher() {}

    public static LogWatcher getInstance() {
        if (instance == null) {
            synchronized (LogWatcher.class) {
                if (instance == null) {
                    instance = new LogWatcher();
                }
            }
        }
        return instance;
    }

    /**
     * Init the logcat watcher.
     *
     * @param context    Application context.
     * @param logDirName Logcat save dir
     * @return LogcatWatcher instance.
     */
    public LogWatcher init(Context context, String logDirName) {
        if (context == null)
            throw new IllegalArgumentException("LogWatcher: init failed, context can not be null");

        if (TextUtils.isEmpty(logDirName))
            throw new IllegalArgumentException("LogWatcher: init failed, logDirName can not be null");

        this.mContext = context.getApplicationContext();
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            File documentFileDir = mContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
            if (documentFileDir != null) {
                sLogDirPath = documentFileDir.getAbsolutePath() + File.separator + logDirName;
            } else {
                Log.e(TAG, "LogWatcher: init LogWatcher failed!");
            }
        } else {
            sLogDirPath = mContext.getFilesDir().getAbsolutePath() + File.separator + logDirName;
        }

        return this;
    }

    /**
     * Start capture the logcat generated by the app.
     */
    public void startWatch() {
        stopWatch();

        if (TextUtils.isEmpty(sLogDirPath)) {
            Log.e(TAG, "LogWatcher: can not watch log, the log dir can not be created");
            return;
        }

        mLogcatFile = createNewLogFile();
        if (mLogcatFile == null) {
            Log.e(TAG, "LogWatcher: can not create new log file");
            return;
        } else {
            Log.i(TAG, "LogWatcher: log file save path >>> " + mLogcatFile.getAbsolutePath());
        }

        // Clear cache log
        try {
            Process process = Runtime.getRuntime().exec("logcat -c");
            process.destroy();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //final String LOGCAT_FILTER_CMD = "logcat -v time *:v | grep "(" + android.os.Process.myPid() + ")" > " + newLogFile.getAbsolutePath();
        final String LOGCAT_FILTER_CMD = "logcat -v time *:V -f " + mLogcatFile.getAbsolutePath();

        try {
            mLogcatProcess = Runtime.getRuntime().exec(LOGCAT_FILTER_CMD);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Stop capture the logcat generated by the app.
     */
    public void stopWatch() {
        if (mLogcatProcess != null) {
            mLogcatProcess.destroy();
            mLogcatProcess = null;
        }

        if (mLogcatFile != null) {
            notifySystemToScan(mContext, mLogcatFile);
        }
    }

    private File createNewLogFile() {
        File logSaveDir = new File(sLogDirPath, getCurrentDateStr());
        if (!logSaveDir.exists()) {
            boolean mkdirs = logSaveDir.mkdirs();
            if (!mkdirs) {
                Log.e(TAG, "LogWatcher: create new save dir failed");
                return null;
            }
        }

        String logFileName = LOG_FILE_PREFIX + getCurrentTimeStr() + LOG_FILE_SUFFIX;
        File logFile = new File(logSaveDir, logFileName);

        try {
            boolean createRet = logFile.createNewFile();
            if (!createRet) {
                Log.e(TAG, "LogWatcher: create new log file failed");
                return null;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        return logFile;
    }

    private static String getCurrentTimeStr() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault());
        return sdf.format(new Date(System.currentTimeMillis()));
    }

    private static String getCurrentDateStr() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
        return sdf.format(new Date(System.currentTimeMillis()));
    }

    private static void notifySystemToScan(Context context, File file) {
        if (context == null || file == null) return;
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri uri = Uri.fromFile(file);
        intent.setData(uri);
        context.sendBroadcast(intent);
    }

}

为了简化使用,我把所有相关的操作都放在一个类里去完成了,这样只需要包含这一个文件即可。notifySystemToScan()的作用是为了在写入完成后能够刷新logcat文件,否则写入完毕后连上电脑在Windows上看不到文件。

默认保存路径为:Android/data/packageName/files/Documents/Logcat目录下。为了方便查看,在保存时我按照日期创建了不同的文件夹对logcat进行保存。

主要实现思想利用了adb logcat -f 这个指令,它可以持续不断的向某个文件中写入数据。

开始记录:

private void startLogWatcher() {
        LogWatcher.getInstance().init(getApplicationContext(), PathDefine.LOG_SAVE_DIR).startWatch();
    }

停止记录:

LogWatcher.getInstance().stopWatch();

运行截图:
android: 记录app运行过程中产生的log第1张

(完)

免责声明:文章转载自《android: 记录app运行过程中产生的log》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux系统下安装JDK及环境配置[转]C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MyBatis 原码解析(version:3.2.7)

mybatis-plus 实践及架构原理.pdf mybatis-plus思维导图 首先,我们看使用原生的JDBC来操作数据库的方式: // 1. 获取JDBC Connection Connection connection = DbManager.getConnectoin(); // 2. 组装sql语句 String sql = "inser...

Java Properties 类读取配置文件信息

在我们平时写程序的时候,有些参数是经常改变的,而这种改变不是我们预知的。比如说我们开发了一个操作数据库的模块,在开发的时候我们连接本地的数据库那么IP,数据库名称,表名称,数据库主机等信息是我们本地的,要使得这个操作数据的模块具有通用性,那么以上信息就不能写死在程序里。通常我们的做法是用配置文件来解决。 各种语言都有自己所支持的配置文件类型。比如Pytho...

从源码看Spring Boot 2.0.1

Spring Boot 命名配置很少,却可以做到和其他配置复杂的框架相同的功能工作,从源码来看是怎么做到的。 我这里使用的Spring Boot版本是 2.0.1.RELEASE Spring Boot最重要的注解: @SpringBootApplication 打开它: 其中的几个注解: @SpringBootConfiguration   标注这个类...

C#中IntPtr

C#中IntPtr System.Object System.ValueType System.IntPtr 1.C#中的IntPtr类型被称之为“平台特定的整数类型”,用于本机资源,例如窗口句柄。 2.资源的大小取决于使用的硬件和操作系统,即此类型的实例在32位硬件和操作系统中将是32位,在64位硬件和操作系统中将是64位;但其大小总是足以包含系统的...

Android-操作系统拨打电话广播的处理

Android操作系统的 packages/apps/phone/AndroidManifest.xml源码阅读 在之前的博客,Android-隐式意图激活操作系统通话界面,讲解了,阅读Android操作系统的 packages/apps/phone/AndroidManifest.xml,是如何暴露的 ...... 等等  Android操作系统的...

c# automapper 使用(一)

一、最简单的用法 有两个类User和UserDto 1 public class User 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 public int Age { get;...