添加crash日志收集功能
package com.example.myapplication.utils;
import android.os.Build;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LogUtils {
//日志级别
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int ASSERT = 6;
//默认TAG,logcat中的TAG
private static final String DEFAULT_TAG = "LogUtils";
//日志最大长度
private static final int MAX_LENGTH = 1024 * 3;
//缓冲区队列大小
private static final int BUFFER_SIZE = 1024;
//日志文件名
private static String sLogFileName;
//本地日志保存路径
private static String sLogFilePath;
//线程池
private static ExecutorService sExecutorService;
/**
* 日志初始化,设置日志文件路径和文件名
* 需要在application的onCreate方法中调用
*
* @param path 日志文件路径
* @param fileName 日志文件名称
*/
public static void init(String path, String fileName) {
sLogFileName = fileName;
sLogFilePath = path;
checkFilePath();
//创建一个大小固定为3的线程池
sExecutorService = Executors.newFixedThreadPool(3);
}
/**
* 检查日志文件目录是否存在,不存在则创建新目录
*/
private static void checkFilePath() {
File logFile = new File(sLogFilePath);
if (!logFile.exists()) {
boolean isSuccess = logFile.mkdirs();
if (!isSuccess) {
Log.e(DEFAULT_TAG, "create log file directory failed.");
}
}
}
/**
* 输出VERBOSE日志
*
* @param msg 日志内容
*/
public static void v(String msg) {
printLog(VERBOSE, DEFAULT_TAG, msg);
}
/**
* 输出VERBOSE日志
*
* @param tag logcat中TAG
* @param msg 日志内容
*/
public static void v(String tag, String msg) {
printLog(VERBOSE, tag, msg);
}
/**
* 输出DEBUG日志
*
* @param msg 日志内容
*/
public static void d(String msg) {
printLog(DEBUG, DEFAULT_TAG, msg);
}
/**
* 输出DEBUG日志
*
* @param tag logcat中TAG
* @param msg 日志内容
*/
public static void d(String tag, String msg) {
printLog(DEBUG, tag, msg);
}
/**
* 输出INFO日志
*
* @param msg 日志内容
*/
public static void i(String msg) {
printLog(INFO, DEFAULT_TAG, msg);
}
/**
* 输出INFO日志
*
* @param tag logcat中TAG
* @param msg 日志内容
*/
public static void i(String tag, String msg) {
printLog(INFO, tag, msg);
}
/**
* 输出WARN日志
*
* @param msg 日志内容
*/
public static void w(String msg) {
printLog(WARN, DEFAULT_TAG, msg);
}
/**
* 输出WARN日志
*
* @param tag logcat中TAG
* @param msg 日志内容
*/
public static void w(String tag, String msg) {
printLog(WARN, tag, msg);
}
/**
* 输出ERROR日志
*
* @param msg 日志内容
*/
public static void e(String msg) {
printLog(ERROR, DEFAULT_TAG, msg);
}
/**
* 输出ERROR日志
*
* @param tag logcat中TAG
* @param msg 日志内容
*/
public static void e(String tag, String msg) {
printLog(ERROR, tag, msg);
}
/**
* 输出ASSERT日志
*
* @param msg 日志内容
*/
public static void a(String msg) {
printLog(ASSERT, DEFAULT_TAG, msg);
}
/**
* 输出ASSERT日志
*
* @param tag logcat中TAG
* @param msg 日志内容
*/
public static void a(String tag, String msg) {
printLog(ASSERT, tag, msg);
}
/**
* 打印日志,并写入到本地
*
* @param level 日志级别
* @param tag logcat中TAG
* @param msg 日志内容
*/
private synchronized static void printLog(final int level, final String tag, final String msg) {
String threadName = Thread.currentThread().getName();
String logStr = getLogStr(level, tag, msg, threadName);
Log.println(level, tag, logStr);
writeLog2File(logStr);
}
/**
* 将日志写入到本地文件
*
* @param logStr 日志内容
*/
private synchronized static void writeLog2File(final String logStr) {
if (sExecutorService.isShutdown()) {
sExecutorService = Executors.newFixedThreadPool(3);
}
sExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
// 检测日志文件大小是否超出阈值,若超出则清空文件
File logFile = new File(sLogFilePath + File.separator + sLogFileName);
if (logFile.length() >= MAX_LENGTH) {
logFile.delete();
}
BufferedWriter bw = new BufferedWriter(new FileWriter(logFile, true));
bw.write(logStr);
bw.flush();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
/**
* 获取完整的日志信息
*
* @param level 日志级别
* @param tag logcat中TAG
* @param msg 日志内容
* @param threadName 线程名
* @return 完整的日志信息
*/
private static String getLogStr(int level, String tag, String msg, String threadName) {
String levelStr = "";
switch (level) {
case VERBOSE:
levelStr = "[V]";
break;
case DEBUG:
levelStr = "[D]";
break;
case INFO:
levelStr = "[I]";
break;
case WARN:
levelStr = "[W]";
break;
case ERROR:
levelStr = "[E]";
break;
case ASSERT:
levelStr = "[A]";
break;
}
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
.format(new Date(System.currentTimeMillis()));
return "[" + time + "] " + levelStr + " " + tag
+ " [" + threadName + "] " + msg + "\n";
}
/**
* 输出应用崩溃日志到本地文件
*
* @param e 异常信息
*/
public static void writeCrashLog(Throwable e) {
File file = new File(sLogFilePath + File.separator + "crash.log");
try {
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream outputStream = new FileOutputStream(file, true);
outputStream.write(getThrowableInfo(e).getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
/**
* 获取异常的堆栈信息
*
* @param throwable 异常信息
* @return 异常的堆栈信息
*/
private synchronized static String getThrowableInfo(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
pw.println("-------------------start-------------------");
pw.println("Time:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
.format(new Date(System.currentTimeMillis())));
pw.println("Model:" + Build.MODEL);
pw.println("Version:" + Build.VERSION.RELEASE);
throwable.printStackTrace(pw);
pw.println("--------------------end--------------------");
return sw.toString();
}
}