有时候我们需要将打包好的apk给远端的客户使用测试,我们就需要将log打印到手机内置的文件夹中,以便于后面自己在解决bug的时候快速定位。不过我们当然不希望自己的项目有何种无理由的bug和致命的崩溃
收集log到文件夹中
- 有时候程序出现异常停止,而logcat 的相关信息很快会被冲掉,因而不能够及时的获取异常信息。(所有logUtil封装的也可以保存)
- 在这推荐单例模式对这个类进行操作
/**
* - Created by Luke on 2017/8/16
*/
public class LogcatManager {
private static LogcatManager INSTANCE = null;
//logcat的路径地址
private static String PATH_LOGCAT;
private LogDumper mLogDumper = null;
private int mPId;
private SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyyMMdd");
private SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static LogcatManager getInstance() {
if (INSTANCE == null) {
INSTANCE = new LogcatManager();
}
return INSTANCE;
}
private LogcatManager() {
mPId = android.os.Process.myPid();
}
private void setFolderPath(String folderPath) {
File folder = new File(folderPath);
if (!folder.exists()) {
folder.mkdirs();
}
if (!folder.isDirectory())
throw new IllegalArgumentException("The logcat folder path is not a directory: " + folderPath);
PATH_LOGCAT = folderPath.endsWith("/") ? folderPath : folderPath + "/";
}
public void start(String saveDirectoy) {
setFolderPath(saveDirectoy);
if (mLogDumper == null)
mLogDumper = new LogDumper(String.valueOf(mPId), PATH_LOGCAT);
mLogDumper.start();
}
public void stop() {
if (mLogDumper != null) {
mLogDumper.stopLogs();
mLogDumper = null;
}
}
private class LogDumper extends Thread {
private Process logcatProc;
private BufferedReader mReader = null;
private boolean mRunning = true;
String cmds = null;
private String mPID;
private FileOutputStream out = null;
public LogDumper(String pid, String dir) {
mPID = pid;
try {
out = new FileOutputStream(new File(dir, "logcat-" + simpleDateFormat1.format(new Date()) + ".log"), true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
cmds = "logcat *:e *:i | grep \"(" + mPID + ")\"";
}
public void stopLogs() {
mRunning = false;
}
@Override
public void run() {
try {
logcatProc = Runtime.getRuntime().exec(cmds);
mReader = new BufferedReader(new InputStreamReader(logcatProc.getInputStream()), 1024);
String line = null;
while (mRunning && (line = mReader.readLine()) != null) {
if (!mRunning) {
break;
}
if (line.length() == 0) {
continue;
}
if (out != null && line.contains(mPID)) {
out.write((simpleDateFormat2.format(new Date()) + " " + line + "\n").getBytes());
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (logcatProc != null) {
logcatProc.destroy();
logcatProc = null;
}
if (mReader != null) {
try {
mReader.close();
mReader = null;
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
out = null;
}
}
}
}
}
收集错误崩溃日志到文件夹中
这个一般是出现程序出现错误的时候才会自动保存到手机里的文件夹中,比如常见的NullPointerException,ArrayIndexOutOfBoundsException,SQLException ... and so on.
这个类的代码量有点多,不利于直观的看出来,在这只贴出重要的逻辑部分
还是推荐单例模式对这个类进行操作
/**
* 收集设备参数信息
*/
public void collectDeviceInfo(Context context) {
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (PackageManager.NameNotFoundException e) {
Logger.getLogger(TAG, "收集包信息时出错");
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
} catch (Exception e) {
Logger.getLogger(TAG, "收集包信息时出错");
}
}
}
-----------------------------------------保存错误信息到文件中---------------------------------------------------------------
/**
* @return 返回文件名称, 便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key).append("=").append(value).append("\n");
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = formatter.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".log";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = "/sdcard/crash_police/";
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(path + fileName);
sb.append("\r\n报错日期:");
sb.append(new Date(System.currentTimeMillis()).toLocaleString()).append("\r\n");
printStackTrace(sb, ex);
fos.write(sb.toString().getBytes());
fos.close();
}
return fileName;
} catch (Exception e) {
Logger.getLogger(TAG, "写入文件时出错...");
}
return null;
}
- 使用方法:
- 建议在自己的Application中进行开启log和Crash的收集
2.在onCreate里开始收集log startLogcat(),和CrashHandler.的初始化就可以了
3.onTerminate() 在程序终止的时候讲logcat的收集关闭,直接调用stopLogcat();就O 了
4.下面是开始和终止的方法
private void startLogcat() {
String folderPath = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
folderPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Police-Logcat";
} else {
folderPath = this.getFilesDir().getAbsolutePath() + File.separator + "Police-Logcat";
}
LogcatManager.getInstance().start(folderPath);
}
private void stopLogcat() {
LogcatManager.getInstance().stop();
}
- 大功告成,经测试是木有任何问题的