Android查看调用函数名与行号等信息的日志类
用C的人一定知道__FILE__、__func__和__LINE__,可以方便的跟踪代码执行到哪个文件里面的那个函数,是第多少行,但是Java中并没有这样的定义,在调试代码时会感觉到很不方便。但是,我们也可以通过一些函数来实现同样的功能。
参考网络上的一些文章,我整理实现了一个日志类,实现如下目标:
- 能实现调试跟踪功能;
- 能打印出类名、方法名、行号;
- 能在发布时很容易的关闭日志;
- 简化在书写代码时添加日志的操作:
a. 在只需要跟踪运行位置时,能简单的拷贝复制;
b. 需要增加描述时,方便添加; - 在查看日志时,能方便的挑选自己感兴趣的日志。
下面我来根据前面定下的目标,逐项来说明如何实现,在文章的最后附上代码。
1,能实现调试跟踪功能;
这个功能,系统提供的android.util.Log就很好的实现了,在我们的类中直接使用,只是再加一层接口封装。
2,能打印出类名、方法名、行号;
打印调用栈是android平台问题定位的基本方法,如果需要知道谁在调用某个函数,可以在此函数中添加打印调用栈函数,弄清楚函数之间的调用关系。我们可以通过查看调用栈来获取类名,方法名,行号等。
Exception e = new Exception();
StackTraceElement[] trace = e.getStackTrace();
获取类名:trace[0].getClassName()
获取方法名:trace[0].getMethodName()
获取行号:trace[0].getLineNumber()
3,能在发布时很容易的关闭日志;
定义一个Log的LEVEL,在每次调用 android.util.Log 之前,先判断LEVEL是否在范围内,若不在,则不输出日志。这样,在平常需要调试时,设置我们的输出级别,在发布时,可以控制不输出日志。只需要修改一处代码即可实现功能。
所有日志级别都输出:
public static final int LEVEL = VERBOSE;
发布时,所有日志都不输出:
public static final int LEVEL = NOTHING;
4,简化在书写代码时添加日志的操作:
a,在只需要跟踪运行位置时,能简单的复制粘贴;
b,需要增加描述时,方便添加;
根据平时添加的习惯,日志的TAG参数,使用类名,在日志的内容里面,添加方法名,以及行号,有了这些信息,就可以很好的确认打印位置了。我实现出来后,在要添加日志的地方就是一句:
LogUtil.logWithMethod(new Exception());
所以,在不需要添加附加信息时,例如,记录某个地方是否执行到,只需要简单的拷贝一份就行了。
考虑到还是有很多地方需要添加消息内容的,就在类里面使用同名函数的重载,后面添加一个String参数。这样,保持了书写习惯上的相似性。示例如下:
LogUtil.logWithMethod(new Exception(),”task id is ” + getTaskId());
5,在查看日志时,能方便的挑选自己感兴趣的日志;
为了方便选择日志,在写消息内容时,添加了一个固定信息:“LogUtil”,当然,也可以修改为其他内容,这个信息就是用于进行日志内容过滤的。对于一个较大的工程,不一定是一个人开发的,那么,不同的人可以使用不同的过滤特征字。而且系统还会有许多其他的日志信息,使用这个我们添加的特征字来过滤,就可以只查看我们关心的内容了。另外,类名,方法名,都可以做为过滤器来使用。
输出信息示例如下:
06-10 08:39:48.278: I/MainActivity(2581): logUtil: onCreate:21
06-10 08:39:48.278: I/MainActivity(2581): logUtil: onCreate:22: task id is 26
其中,简单类名“MainActivity”可以用作TAG过滤,而内容特征字“logUtil”以及函数名“onCreate”都可以用作内容过滤。
该日志类的实现代码具体如下:
package com.example.logutil;
import java.util.StringTokenizer;
import android.util.Log;
/**
* The Class LogUtil for log printing, which help us
* easy to trace our codes or logics in the project .
* @author lintax
* @time 2016.6.10
*/
public class LogUtil {
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 NOTHING = 6;
private static final int LEVEL = VERBOSE;// control the output info level
private static final String logContentFilter = "logUtil";
public static void v(String tag, String msg) {
if (LEVEL <= VERBOSE) {
Log.v(tag, msg);
}
}
public static void d(String tag, String msg) {
if (LEVEL <= DEBUG) {
Log.d(tag, msg);
}
}
public static void i(String tag, String msg) {
if (LEVEL <= INFO) {
Log.i(tag, msg);
}
}
public static void w(String tag, String msg) {
if (LEVEL <= WARN) {
Log.w(tag, msg);
}
}
public static void e(String tag, String msg) {
if (LEVEL <= ERROR) {
Log.e(tag, msg);
}
}
public static void logWithMethod(Exception e) {
StackTraceElement[] trace = e.getStackTrace();
if (trace == null || trace.length == 0) {
i("error", "log: get trace info failed");
}
String class_name = getSimpleClassName(trace[0].getClassName());
i(class_name, logContentFilter + ": " + trace[0].getMethodName()
+ ":" + trace[0].getLineNumber());
}
public static void logWithMethod(Exception e, String msg) {
StackTraceElement[] trace = e.getStackTrace();
if (trace == null || trace.length == 0) {
i("error", "log: get trace info failed");
}
String class_name = getSimpleClassName(trace[0].getClassName());
i(class_name, logContentFilter + ": " + trace[0].getMethodName()
+ ":" + trace[0].getLineNumber() + ": " + msg);
}
public static String getSimpleClassName(String fullClassName) {
String split = ".";
String class_name = "";
StringTokenizer token = new StringTokenizer(fullClassName, split);
while (token.hasMoreTokens()) {
class_name = token.nextToken();
}
return class_name;
}
}