Android 导出APP日志
本文介绍如何抓取APP全局日志,并将其保存到私有目录。
1.创建一个抓取日志服务
public class LogcatService extends Service {
private static final String THIS_FILE = "TMC";
private boolean stop = false;
private int maxFileCount = 4;
private long maxFileSize = 32*1024*1024;
private static final String BACKUP_PREFIX = "log_backup_";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
boolean success = LogcatUtil.startCapture();
if (!success) {
Log.e(THIS_FILE, "Error to start capture logcat!");
} else {
// 启动监听线程,因为子进程会被系统回收所以需要监听
new Thread(new Runnable() {
@Override
public void run() {
while(!stop) {
File logFile = LogcatUtil.getLogFile();
if (logFile.length() >= maxFileSize) {
Log.d(THIS_FILE, "Log file size exceed! Backup file");
LogcatUtil.stopCapture();
backupLogFile(logFile);
LogcatUtil.startCapture();
}
Process process = LogcatUtil.getProcess();
boolean isAlive = false;
try {
int exitValue = process.exitValue();
if (exitValue != 0) {
isAlive = false;
}
} catch (IllegalThreadStateException e) {
// e.printStackTrace();
isAlive = true;
}
if (!isAlive) {
Log.e(THIS_FILE, "Logcat thread died!!!");
LogcatUtil.resumeCapture();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(THIS_FILE, "Logcat thread normal stop!!!");
}
}).start();
}
}
private void backupLogFile(File logFile) {
String logPath = LogcatUtil.getFilePath();
if (!new File(logPath).exists()) {
Log.e(THIS_FILE, "Error!! Log file path not exists");
return;
}
for (int index =0;index<maxFileCount;index++) {
String backupName = logPath + BACKUP_PREFIX + index + ".log";
File file = new File(backupName);
if (!file.exists()) {
Log.d(THIS_FILE, "Backup file " + logFile.getAbsolutePath() + " to " + backupName);
logFile.renameTo(file);
return;
}
}
Log.d(THIS_FILE, "Backup file is full. Delete first and rename current log file");
String firstBackupName = logPath + BACKUP_PREFIX + "0.log";
File firtFile = new File(firstBackupName);
firtFile.delete();
for (int index =1;index<maxFileCount;index++) {
String backupName = logPath + BACKUP_PREFIX + index + ".log";
File file = new File(backupName);
file.renameTo(new File(logPath + BACKUP_PREFIX + (index-1) + ".log"));
}
// backup current file
String currentBackupName = logPath + BACKUP_PREFIX + (maxFileCount-1) + ".log";
logFile.renameTo(new File(currentBackupName));
}
@Override
public void onDestroy() {
super.onDestroy();
stop = true;
LogcatUtil.stopCapture();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
2.日志处理类
包括初始化文件目录、创建文件、抓取日志的指令等相关操作。
public class LogcatUtil {
private static final String THIS_FILE = "LogcatUtil";
private static final String TEMP_LOG = "tmclog-" + getFileName() + ".log";
private static String filePath = "";
private static File logFile = null;
private static Process process = null;
private static boolean running = false;
public static String getFilePath() {
return filePath;
}
public static void setFilePath(String path) {
filePath = path;
}
public static Process getProcess() {
return process;
}
public static File getLogFile() {
return logFile;
}
/**
* 初始化目录
*/
public static void init(Context context) {
try {
File sdDir = context.getExternalFilesDir(null);
filePath = sdDir.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
Log.e("CoverCard","创建文件失败!");
}
}
public static String getFileName() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String date = format.format(new Date(System.currentTimeMillis()));
return date;
}
// 清除4天前保存的logcat日志
public static void cleanHistory() {
FileUtil.deleteFilesBefore(filePath, 4 * 24 * 60 * 60 * 1000);
}
public static boolean startCapture() {
LogcatUtil.cleanHistory();
if (running) {
Log.d(THIS_FILE, "Loggcat capture is already running");
return true;
}
File path = new File(filePath);
if (!path.exists()) {
path.mkdirs();
}
logFile = new File(path, TEMP_LOG);
try {
process = Runtime.getRuntime().exec("logcat -c");
process = Runtime.getRuntime().exec("logcat -f " + logFile);
} catch (IOException e) {
Log.e(THIS_FILE, "Trye to capture app logcat failed. IO exception");
e.printStackTrace();
return false;
}
running = true;
return true;
}
/**
* 继续抓取日志,使用同样的文件
*
* @return
*/
public static boolean resumeCapture() {
if (logFile != null) {
try {
process = Runtime.getRuntime().exec("logcat -c");
process = Runtime.getRuntime().exec("logcat -f " + logFile);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
public static void stopCapture() {
if (process != null) {
process.destroy();
}
logFile = null;
running = false;
}
}
3.调用类
public class LogModule {
public static void init(Context context) {
LogcatUtil.init(context);
}
/**
* 应用启动抓取系统全日志
*
* @param context
*/
public static void openCollectSystemLog(Context context) {
Intent intent = new Intent(context, LogcatService.class);
context.startService(intent);
}
/**
* 停止日志收集
*
* @param context
*/
public static void closeCollectSystemLog(Context context) {
Intent intent = new Intent(context, LogcatService.class);
context.stopService(intent);
}
}
4.使用方法
- 首先将服务在AndroidManifest.xml中,进行注册。
<service android:name="com.demo.logutils.LogcatService"
android:enabled="true"
android:exported="true" />
- 在activity中使用LogModule进行调用。
@Override
protected void onCreate(Bundle savedInstanceState) {
<-----省略部分代码----->
LogModule.init(this);// 初始化
LogModule.openCollectSystemLog(this);// 启动抓取系统全日志
}
@Override
protected void onDestroy() {
super.onDestroy();
LogModule.closeCollectSystemLog(this);
}