Android动态抓取Log
在实际开发过程中,当应用遇到问题,崩溃,Logcat常常能帮助开发者快速定位问题。在开发过程中,一款好的logcat设计,能让以后的维护变得简单快速。网络上也有很多开源的日志项目,log4j等等,也有很多第三方服务商提供抓取log的功能。当用户在使用app过程中应用发生崩溃,anr等事件时,快速抓取现场log并上报。下面我们就自己实现这一功能!
原理简单介绍:
利用adb命令:logcat,能从手机的log缓存文件中读取logcat。这样读出来缓存区所有的log,并不是我们想要的,这时参数便派上用场!
关于参数的详解,大家可以参照这篇博客:adb logcat命令
我们这里关键利用 “-t”: 输出最近的几行日志, 输出完退出, 不阻塞;
当崩溃发生时,取得最近的N行log就行。(注:log里并不包含错误崩溃的堆栈日志)
1、Android 4.1以下如果manifest文件中没有配置READ_LOGS权限将没有log输出
2、由于很多手机厂商做了修改,某些机型需要在设置中打开logcat开关才能取到log,本人实际测试中华为机型就是如此。具体操作:拨号界面输入:*#*#2846579#*#*进入测试菜单界面
异常监控:
我们需要先加入监控程序崩溃的代码,触发我们的捕获logcat操作。直接上代码:
currentHandler= Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
//开始捕获logcat操作
readLogs();
//必须交还异常操作
currentHandler.uncaughtException(thread,ex);
}
});
这样,当系统发生崩溃时就会回调我们的uncaughtException接口,如果想获取堆栈等信息,也可以在这里面进行!
捕获logcat
现在就是拿现场logcat的操作了。同理,上代码:
public static String readLogs() {
Process localProcess = null;
final StringBuilder stringBuilder = new StringBuilder();
//这里读取50行。第四个string可以拿来做过滤条件,只取想要的层级log
//可以是 *:V, *:D ,*:I ,*:W ,*:E ,*:F, *:S
//也可以指定标签式的过滤(如:tagName:D *:S),":D"表示log的层级!!
//当然也可以多个过滤器(如:tagName1:D tagName2:W tagName3:I *:S)
String[] pa = {"logcat", "-t", "50", ""};
try {
localProcess = Runtime.getRuntime().exec(pa);
final InputStream is = localProcess.getInputStream();
final InputStream is1 = localProcess.getErrorStream();
new Thread(new Runnable() {
@Override
public void run() {
String logMessage;
BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
try {
while ((logMessage = localBufferedReader.readLine()) != null) {
stringBuilder.append(logMessage);
stringBuilder.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (localBufferedReader != null) {
localBufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
BufferedReader br2 = new BufferedReader(
new InputStreamReader(is1));
try {
String lineC;
while ((lineC = br2.readLine()) != null) {
stringBuilder.append(lineC);
stringBuilder.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is1 != null) {
is1.close();
}
if (br2 != null) {
br2.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
localProcess.waitFor();
} catch (Exception localException) {
return localException.getMessage();
} finally {
try {
if (localProcess != null) {
localProcess.destroy();
}
} catch (Exception e) {
}
}
return stringBuilder.toString();
代码比较糙,建议大家自己手动敲一遍,然后重构一下,还可以对取到的字节数做限定。比如:只取5kb的logcat数据等等。虽然是两个线程去读取流和错误流,但这个进程是阻塞式读取。建议大家读取行数的参数不要设置太大,如2000,10000……这样不但没有意义,而且在系统底层C操作logcat的时候会写入很多error信息到logcat中。所以实际开发中应该对最大行数做限定!