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();
运行截图:
(完)