虽然有单步调试这个强大的工具来帮助程序员找出程序中的错误,但是log在某些情况下是单步调试所不能取代的。比如获取用户实际使用时发生错误的log来诊断程序的问题等。
为了能让log更好的帮助我们诊断程序的问题,如何加log是有讲究的。
1. 每条log中需要包含输出log的级别,源文件,函数名,行号。这些信息可以帮助查看log的人员定位出这句log是从哪里打印的。
2. log是分级别(level)的,以android.util.Log为例。Log的级别以log出现的频度由低到高(出现频度越低,log级别越高)分为ERROR, WARN, INFO,DEBUG, VERBOSE。
3. log分级的好处
a) 诊断问题时,可以先查看有没有ERROR,WARN级别的log,快速定位问题。
b) 通过在release版本中,提高log级别来过滤掉一些不重要,但又数量众多的log。比如release版本中,log级别设为INFO,那么DEBUG,VERBOSE级别的log就不会输出。
4. 每个log级别的含义
a) ERROR级别的log适合加在发生重大的Exception,导致程序不能正常运行
b) WARN级别的log适合加在虽然发生一些Exception,但是程序还可以继续运行
c) INFO级别的log适合于告诉外界程序运行阶段的重要的运行过程信息
d) DEBUG级别的log用于在调试过程中提供丰富的信息
e) VERBOSE级别的log一般用于会大量出现的log,比如说几个毫秒就会被执行的语句,如打印“心跳”信息。
如下的代码对android.util.Log进行简单封装,在每个log输出中增加了类名,函数名,行号信息
package util;
public class Log {
private static final String CLASS_FORMAT = "[%50s]";
private static final String SIMPLE_CLASS_FORMAT = "[%25s ";
private static final String LINE_FORMAT = "%4s# ";
private static final String METHOD_FORMAT = "%20s]";
public static void v(String message) {
StackTraceElement[] stacks = (new Throwable()).getStackTrace();
android.util.Log.v(getTAG(stacks, message),
"[V]" + getMessage(stacks, message));
}
public static void i(String message) {
StackTraceElement[] stacks = (new Throwable()).getStackTrace();
android.util.Log.i(getTAG(stacks, message),
"[I]" + getMessage(stacks, message));
}
public static void d(String message) {
StackTraceElement[] stacks = (new Throwable()).getStackTrace();
android.util.Log.d(getTAG(stacks, message),
"[D]" + getMessage(stacks, message));
}
public static void w(String message) {
StackTraceElement[] stacks = (new Throwable()).getStackTrace();
android.util.Log.w(getTAG(stacks, message),
"[W]" + getMessage(stacks, message));
}
public static void e(String message) {
StackTraceElement[] stacks = (new Throwable()).getStackTrace();
android.util.Log.e(getTAG(stacks, message),
"[E]" + getMessage(stacks, message));
}
private static String getTAG(StackTraceElement[] stacks, String message) {
if (stacks != null && stacks.length >= 2) {
return String.format(CLASS_FORMAT, stacks[1].getClassName());
} else {
return "";
}
}
private static String getMessage(StackTraceElement[] stacks, String message) {
if (stacks != null && stacks.length >= 2) {
StackTraceElement stack = stacks[1];
String className = stack.getClassName();
String simpleName = null;
int dollarIndex = className.lastIndexOf('$');
if (dollarIndex != -1) {
simpleName = className
.substring(className.lastIndexOf('$') + 1);
} else {
simpleName = className
.substring(className.lastIndexOf('.') + 1);
}
return String.format(SIMPLE_CLASS_FORMAT, simpleName)
+ String.format(LINE_FORMAT, stack.getLineNumber())
+ String.format(METHOD_FORMAT, stack.getMethodName()) + " "
+ message;
} else {
return message;
}
}
}
输出的log类似如下格式
[D][ TestAAAABBBBBBB 89# notifyXXXXXXXXXXXX] enter
[I][ TestAAAABBBBBBB 103# notifyXXXXXXXXXXXX] begin to open conntection
[D][ TestAAAABBBBBBB 37# run] enter
[D][ TestAAAABBBBBBB 62# run] leave
[D][ TestAAAABBBBBBB 162# notifyXXXXXXXXXXXX] leave