保存Android日志到文件
程序员文章站
2022-03-09 16:16:55
...
前言
之前写过一篇日志,《不是安卓工程师,也能看logcat日志》,不仅需要adb环境还要熟悉logcat命令,感觉比较繁琐。现推出升级版,在Service中保存日志服务。同样用到命令logcat -f logpath
。在这之前,已有人造过同样的*。只是代码更长,功能更完善。比如:监听日志文件大小,超出10M就重新生成日志文件。个人认为,这些功能有些画蛇添足。试问,有谁会从10M日志文件中去找调试信息。so,出一个简化版,方便阅读、方便理解。
PS命令
此PS,非PhotoShop。熟悉linux 都知道,ps 命令可以list出系统中所有正在运行的线程。
图示是在adb shell中,list出当前连接设备正在运行的所有进程信息。
grep参数做过滤操作,之前在查看指定pid的日志中有提过。(Android 7.0 开始logcat 支持pid 筛选,如:logcat --pid=进程id
)。从上图中,可以看出:
进程A 创建进程B
A.USER == B.USER
A.PID == B.PPID
同样,进程A创建的logcat进程只能保存进程A 的全部日志,执行的PS命令只能list出同USER属性相同的进程。
保存日志
/**
* 开始保存日志
*/
private void startSaveLog() {
try {
Runtime.getRuntime().exec("logcat -f " + logFilePath);
//获取新生成的logcat 进程信息
logcatInfo = getLogcatProInfo(getProcessInfoStr("logcat"));
} catch (IOException e) {
e.printStackTrace();
}
}
清除日志缓存
/**
* 清除日志缓存
*/
private void clearLogCache() {
try {
//清除日志缓存
Runtime.getRuntime().exec("logcat -c");
} catch (IOException e) {
e.printStackTrace();
}
}
在开始保存日志前调用,清除之前已经输出的日志。
关闭进程
/**
* 删除logcat进程,退出保存日志
*/
private void killLogcat() {
if (logcatInfo == null)
return;
try {
Runtime.getRuntime().exec("kill " + logcatInfo.pid);
} catch (IOException e) {
e.printStackTrace();
}
}
可以在任意位置发送广播,停止保存日志。
广播控制日志保存和关闭
/**
* 保存日志操作广播
*/
class SaveLogReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(KILL_LOGCAT_ACTION)) {
killLogcat();
} else if (intent.getAction().equals(START_LOGCAT_ACTION)) {
clearLogCache();
startSaveLog();
}
}
}
同样,可以在任意地方重开始,继续保存日志。这样可以有效避免保存了无用的日志信息,使日志内容又臭又长。
获取当前应用的进程信息
appProInfo = new ProcessInfo(getProcessInfoStr(getPackageName()).get(0));
/**
* 根据名称获取线程信息
*
* @param name
* @return 返回线程信息的字符串格式
*/
private ArrayList<String> getProcessInfoStr(String name) {
ArrayList<String> strs = new ArrayList<String>();
try {
java.lang.Process process = Runtime.getRuntime().exec("ps");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
if (line.endsWith(name)) {
strs.add(line);
}
}
br.close();
isr.close();
is.close();
br = null;
isr = null;
is = null;
} catch (IOException e) {
e.printStackTrace();
}
return strs;
}
/**
* 进程信息,PS命令获取
*/
class ProcessInfo {
private String user;
private String pid;
private String ppid;
private String vsize;
private String rss;
private String wchan;
private String pc;
private String name;
public ProcessInfo(String info) {
if (info == null)
return;
//info 可能连续包含多个空格,因此用split方法无效。
ArrayList<String> item = new ArrayList<String>();
for (int i = 0; i < info.length(); i++) {
if (info.charAt(i) == ' ') {
continue;
}
int j = i + 1;
while (j < info.length() && info.charAt(j++) != ' ') ;
item.add(info.substring(i, j - 1));
i = j - 1;
}
user = item.get(0);
pid = item.get(1);
ppid = item.get(2);
vsize = item.get(3);
rss = item.get(4);
wchan = item.get(5);
pc = item.get(6);
name = item.get(8);
}
}
使用方法
1、在AndroidManife.xml里注册Service
2、启动Service,开始保存日志
3、发送广播,停止保存日志
4、发送广播,继续保存日志
最后附上源码:
/**
* 保存日志服务
* Created by 左克飞 on 17/8/3 下午3:25.
* e-mail:aaa@qq.com
* FilePath App:com.flueky.framework.android
*/
public class SaveLogService extends Service {
private String logFoldPath;//日志文件夹路径
private String logFilePath;//日志文件路径
public static final String KILL_LOGCAT_ACTION = "com.flueky.action.kill_logcat";
public static final String START_LOGCAT_ACTION = "com.flueky.action.start_logcat";
private ProcessInfo appProInfo;//应用进程信息
private ProcessInfo logcatInfo;//日志进程信息
private SaveLogReceiver saveLogReceiver;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
logFoldPath = getExternalCacheDir().getAbsolutePath() + "/../log";
saveLogReceiver = new SaveLogReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(KILL_LOGCAT_ACTION);
filter.addAction(START_LOGCAT_ACTION);
registerReceiver(saveLogReceiver, filter);
}
/**
* 校验日志目录文件夹
*
* @return
*/
private boolean checkLogFolder() {
File file = new File(logFoldPath);
if (!file.exists()) {
return file.mkdirs();
}
return true;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
appProInfo = new ProcessInfo(getProcessInfoStr(getPackageName()).get(0));
//清除日志缓存
clearLogCache();
//开始保存日志
startSaveLog();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(saveLogReceiver);
}
/**
* 根据名称获取线程信息
*
* @param name
* @return 返回线程信息的字符串格式
*/
private ArrayList<String> getProcessInfoStr(String name) {
ArrayList<String> strs = new ArrayList<String>();
try {
java.lang.Process process = Runtime.getRuntime().exec("ps");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
if (line.endsWith(name)) {
strs.add(line);
}
}
br.close();
isr.close();
is.close();
br = null;
isr = null;
is = null;
} catch (IOException e) {
e.printStackTrace();
}
return strs;
}
/**
* 获取保存日志的进程信息
*
* @param logcatProcessInfos
* @return
*/
private ProcessInfo getLogcatProInfo(ArrayList<String> logcatProcessInfos) {
if (logcatProcessInfos == null)
return null;
for (String infoStr : logcatProcessInfos) {
ProcessInfo info = new ProcessInfo(infoStr);
if (info.user.equals(appProInfo.user)) {
return info;
}
}
return null;
}
/**
* 删除logcat进程,退出保存日志
*/
private void killLogcat() {
if (logcatInfo == null)
return;
try {
Runtime.getRuntime().exec("kill " + logcatInfo.pid);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 清除日志缓存
*/
private void clearLogCache() {
try {
//清除日志缓存
Runtime.getRuntime().exec("logcat -c");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 开始保存日志
*/
private void startSaveLog() {
try {
if (checkLogFolder())
logFilePath = logFoldPath + "/HH-" + new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime()) + ".log";
Runtime.getRuntime().exec("logcat -f " + logFilePath);
logcatInfo = getLogcatProInfo(getProcessInfoStr("logcat"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 进程信息,PS命令获取
*/
class ProcessInfo {
private String user;
private String pid;
private String ppid;
private String vsize;
private String rss;
private String wchan;
private String pc;
private String name;
public ProcessInfo(String info) {
if (info == null)
return;
ArrayList<String> item = new ArrayList<String>();
for (int i = 0; i < info.length(); i++) {
if (info.charAt(i) == ' ') {
continue;
}
int j = i + 1;
while (j < info.length() && info.charAt(j++) != ' ') ;
item.add(info.substring(i, j - 1));
i = j - 1;
}
user = item.get(0);
pid = item.get(1);
ppid = item.get(2);
vsize = item.get(3);
rss = item.get(4);
wchan = item.get(5);
pc = item.get(6);
name = item.get(8);
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(user).
append(" ").
append(pid).
append(" ").
append(ppid).
append(" ").
append(vsize).
append(" ").
append(rss).
append(" ").
append(wchan).
append(" ").
append(pc).
append(" ").
append(name);
return buffer.toString();
}
}
/**
* 保存日志操作广播
*/
class SaveLogReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(KILL_LOGCAT_ACTION)) {
killLogcat();
} else if (intent.getAction().equals(START_LOGCAT_ACTION)) {
clearLogCache();
startSaveLog();
}
}
}
}