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

有时在解决问题时,经常需要借助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();

运行截图:

(完)

posted @ 2020-07-14 13:46  夜行过客  阅读(1828)  评论(0编辑  收藏  举报