在程序开发过程中,需要记录程序运行中的运行日志。同时很多时候,程序运行中可能会遇到种种异常导致崩溃,在使用AndroidStudio调试过程中,可以在logcat中查看这些异常。但是apk安装在用户手机上,就需要程序自己将这些异常进行捕获并记录在日志中,供开发人员后续进行问题排查。
一、日志记录
1、Timber
一个简单的日志类,Timber把一种日志记录方法看做一颗树tree,Timber则是管理着一片森林,具体树怎么种(日志如何记录),使用哪棵树则由你决定。Timber默认自带的是DebugTree这个类。
step1:在build.gradle中引入
implementation 'com.jakewharton.timber:timber:4.7.1'
GitHub:https:///JakeWharton/timber
step2:根据官方demo,应在application的onCreate中添加下述代码:
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new DebugTree());
} else {
Timber.plant(new CrashReportingTree());
}
}
其中CrashReportingTree这个类,Timber并没有实现,这段代码只是告诉你,你可以自己定义一个tree来进行日志记录。
那么接下来我们自己来定义一棵树。在使用中,我需要将日志实时保存在文件中供后续使用,因此需要引入一个将日志记录在文件中的工具类logback-android。
2、logback-android
step1:在build.gradle中引入
compile 'org.slf4j:slf4j-api:1.7.25'
compile 'org.litepal.android:core:2.0.0'
GitHub:https:///tony19/logback-android
step2:在assets里创建一个logback.xml文件,定义日志文件生成规则。
step3:配置logback.xml文件(将日志保存在手机外部存储中)
<!--debug属性用来决定是否打印logback的日志信息-->
<configuration debug='false'>
<!--声明一个属性,用来指定log文件存放的路径 此处使用外部存储路径-->
<property name="LOG_DIR" value="${EXT_DIR}/log"/>
<!--声明一个时间戳-->
<timestamp datePattern="yyyyMMdd" key="today"/>
<!--用于在控制台输出的Appender-->
<appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
<encoder>
<pattern>%-5relative [%thread][%file:%M:%line] - %msg%n</pattern>
</encoder>
</appender>
<!--声明一个FileAppender-->
<appender name="BASE_FILE" class="ch.qos.logback.core.FileAppender">
<!--初始化的时候不创建文件,在第一次使用的时候创建文件-->
<lazy>true</lazy>
<!--log追加到文件,否则覆盖文件-->
<append>true</append>
<!--用来保存log的文件全路径 /log/-->
<file>${LOG_DIR}/base.log</file>
<!--输出log的格式-->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n</pattern>
</encoder>
</appender>
<!--声明一个RollingFileAppender-->
<appender name="BASE_ROLL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/base.roll.${today}.log</file>
<append>true</append>
<encoder>
<pattern>%date %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/base.roll.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--最大保存7天的日志-->
<maxHistory>7</maxHistory>
</rollingPolicy>
<!--文件大于10mb,切换文件-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
</appender>
<!--指定采用BASE_ROLL_FILE声明的RollingFileAppender输出日志-->
<logger name="admin">
<appender-ref ref="BASE_ROLL_FILE"/>
</logger>
</configuration>
3、使用logback来定义FileLoggingTree
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timber.log.Timber;
public class FileLoggingTree extends Timber.DebugTree{
static Logger mLogger = LoggerFactory.getLogger(FileLoggingTree.class);
@Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE) {
return;
}
String logMessage = tag + ": " + message;
switch (priority) {
case Log.DEBUG:
mLogger.debug(logMessage);
break;
case Log.INFO:
(logMessage);
break;
case Log.WARN:
mLogger.warn(logMessage);
break;
case Log.ERROR:
mLogger.error(logMessage);
break;
}
}
}
4、程序中的使用
step1:为了方便在程序中使用,将上述代码封装在MyLogger类中。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timber.log.Timber;
public class MyLogger {
private static Logger logger =null;
//标识日志类是否进行初始化
private static boolean isInit=false;
//初始化timber,并声明使用FileLoggingTree
public static void initLogger(){
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
} else {
Timber.plant(new FileLoggingTree());
}
isInit = true;
}
public static Logger getLogger(){
if(!isInit){
initLogger();
}
if(logger==null){
//按照logback.xml文件中声明的方式进行日志存储
logger = LoggerFactory.getLogger("admin");
}
return logger;
}
}
step2:在程序任意地方,调用下述代码,完成日志记录。
MyLogger.getLogger().trace("XXXXXX");
MyLogger.getLogger().debug("XXXXXX");
MyLogger.getLogger().info("XXXXXX");
MyLogger.getLogger().error("XXXXXX");
MyLogger.getLogger().warn("XXXXXX");
二、崩溃信息
Android 的Thread 类中提供了一个方法 setDefaultUncaughtExceptionHandler,可以捕获程序运行中的异常。当崩溃发生的时候,系统就会回调 UncaughtExceptionHandler 的 uncaughtException 方法,在 uncaughtException 方法中可以获取到异常信息,可以选择把异常信息存储到logback.xml中指定路径。
step1:我们需要继承Thread.UncaughtExceptionHandler 接口,重写其 uncaughtException 方法,在该方法中获取异常信息并将其保存在log文件中。
import android.util.Log;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
public class CrashHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
//获取崩溃信息
String stackTraceInfo = getStackTraceInfo(throwable);
//将崩溃信息记录
saveThrowableMessage(stackTraceInfo);
}
/**
* 获取错误的信息
*/
private String getStackTraceInfo(final Throwable throwable) {
PrintWriter pw = null;
Writer writer = new StringWriter();
try {
pw = new PrintWriter(writer);
throwable.printStackTrace(pw);
} catch (Exception e) {
return "";
} finally {
if (pw != null) {
pw.close();
}
}
return writer.toString();
}
/**
* 记录奔溃信息到log文件
*/
private void saveThrowableMessage(String errorMessage) {
MyLogger.getLogger().trace(errorMessage);
}
}
step2:然调用 Thread 的 setDefultUncaughtExceptionHandler 方法将它设置为线程默认的异常处理器。
public class MainActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//初始化异常崩溃捕捉线程
CrashHandler handler = new CrashHandler();
Thread.setDefaultUncaughtExceptionHandler(handler);
}
}
-------------------------