Log日志打印工具类
转载需注明出处http://blog.csdn.net/fight_0513/article/details/77098225
概述
- android的日志信息打印为我们开发过程中提供了许多帮助,不过系统的log还不够完美,例如不能快速定位代码位置,每次都要考虑设置怎样的Tag,还不美观,超长字符串打印不全(log单条打印长度限制4*1024字符长度),不能一眼就找到自己打印的信息……
- 网上已经有许多优秀的LOG框架 但是个人觉得有些臃肿,所以想自己动手写个方便的Log工具
个人需求
- 能定位(点击日志跳转到具体代码位置)
- 自动的日志打印开关(发布release版本时自动关闭日志打印)
- 美观(带边宽,可控制)
- 可以打印JSon数据,保证信息完整
- 不需要手动设置TAG
- 支持传入Objec
- 在手动设置TAG的情况下支持输入多个参数
- 提供线程信息
不支持打印到文件,不支持打印XML
执行效果
功能提要
日志工具类的技术含量还是比较低的值得一提的只有快速定位代码位置的功能,这里涉及一个 StackTraceElement 类 就是堆栈信息;通过这个类可以获取当前堆栈信息,例如当前运行的线程,文件名,方法名,类名和代码所在行数……
//获取堆栈信息
// 这里的数组的index=5是根据你工具类的层级做不同的定义,遍历之后就知道啦
StackTraceElement[] targetElement = Thread.currentThread().getStackTrace();
String fileName = targetElement[5].getFileName();
String className = targetElement[5].getClassName();
int lineNum = targetElement[5].getLineNumber();
String methName = targetElement[5].getMethodName();
Log.e("TAG", "processContents: >>>" + fileName);
Log.e("TAG", "processContents: >>>" + className);
Log.e("TAG", "processContents: >>>" + methName);
Log.e("TAG", "processContents: >>>" + lineNum);
//严格按(FileName:LineNuber)的格式来写 才可以定位
Log.e("TAG", "processContents: >>>" + "(" + fileName + ":" + lineNum + ")");
打印结果
注意
调用 BuildConfig 导包时一定要导入 当前 Module 的包,导入其他包 BuildConfig.DEBUG的值将会一直为false
原因:BuildConfig.java 是编译时自动生成的,并且每个 Module 都会生成一份,以该 Module 的 packageName 为 BuildConfig.java 的 packageName。所以如果你的应用有多个 Module或者有其他三方依赖 就会有多个 BuildConfig.java 生成,而上面的 Lib Module import 的是自己的 BuildConfig.java,编译时被依赖的 Module 默认会提供 Release 版给其他 Module 或工程使用,这就导致该 BuildConfig.DEBUG 会始终为 false。
完整代码
代码注释比较详细 如有疑问欢迎留言
public final class mLogUtils {
private mLogUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
private static final int V = 0x01;
private static final int D = 0x02;
private static final int I = 0x04;
private static final int W = 0x08;
private static final int E = 0x10;
private static final int A = 0x20;
private static final int JSON = 0xF2;
/**
* 注意:BuildConfig 导包时一定要导入 当前 Module 的包,导入其他包 BuildConfig.DEBUG的值将会一直为false
*/
private static boolean isLog = BuildConfig.DEBUG; // log总开关 发布release版本时, BuildConfig.DEBUG 为false;
private static String sGlobalTag = null; // log标签
private static boolean sTagIsSpace = true; // log标签是否为空白
private static boolean isLogBorder = true; // log边框开关
private static final String TOP_BORDER = "┱┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄";
private static final String LEFT_BORDER = "┃ ";
private static final String BOTTOM_BORDER = "┹┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄";
private static final String LINE_SEPARATOR = System.getProperty("line.separator");//等同"\n",没有 Windows和Linux的区别
private static final int MAX_LEN = 1024; //单条log打印长度限制为 4*1024个字符长度 网上多把这个值设置为4000,经测试发现还是会丢失内容
private static final String NULL_TIPS = "Log with null object.";
private static final String NULL = "null";
private static final String ARGS = "args";
/**
* @param contents 不需要输入Tag 以类名为Tag
*/
public static void v(Object contents) {
log(V, sGlobalTag, contents);
}
/**
* @param tag 输入Tag
* @param contents 打印内容
*/
public static void v(String tag, Object... contents) {
log(V, tag, contents);
}
public static void d(Object contents) {
log(D, sGlobalTag, contents);
}
public static void d(String tag, Object... contents) {
log(D, tag, contents);
}
public static void i(Object contents) {
log(I, sGlobalTag, contents);
}
public static void i(String tag, Object... contents) {
log(I, tag, contents);
}
public static void w(Object contents) {
log(W, sGlobalTag, contents);
}
public static void w(String tag, Object... contents) {
log(W, tag, contents);
}
public static void e(Object contents) {
log(E, sGlobalTag, contents);
}
public static void e(String tag, Object... contents) {
log(E, tag, contents);
}
public static void a(Object contents) {
log(A, sGlobalTag, contents);
}
public static void a(String tag, Object... contents) {
log(A, tag, contents);
}
public static void json(String contents) {
log(JSON, sGlobalTag, contents);
}
public static void json(String tag, String contents) {
log(JSON, tag, contents);
}
private static void log(int type, String tag, Object... contents) {
if (!isLog) return;
final String[] processContents = processContents(type, tag, contents);
tag = processContents[0];
String msg = processContents[1];
switch (type) {
case V:
case D:
case I:
case W:
case E:
case A:
printLog(type, tag, msg);
break;
case JSON:
printLog(D, tag, msg);
break;
}
}
/**
* @param type
* @param tag
* @param contents
* @return
*/
private static String[] processContents(int type, String tag, Object... contents) {
//获取堆栈信息
// 这里的数组的index=5是根据你工具类的层级做不同的定义,遍历之后就知道啦
StackTraceElement[] targetElement = Thread.currentThread().getStackTrace();
String fileName = targetElement[5].getFileName();
String mTag = fileName.substring(0, fileName.indexOf("."));
if (!sTagIsSpace) {// 如果全局tag不为空,那就用全局tag
tag = sGlobalTag;
} else {// 全局tag为空时,如果传入的tag为空那就显示类名,否则显示tag
tag = isSpace(tag) ? mTag : tag;
}
String headMsg = new Formatter()//严格按(FileName:LineNuber)的格式来写 才可以定位
.format("Thread: %s, %s`(%s:%d)" + LINE_SEPARATOR,
Thread.currentThread().getName(),
targetElement[5].getMethodName(),
fileName,
targetElement[5].getLineNumber())
.toString();
//打印内容部分
String msg = NULL_TIPS;
if (contents != null) {
if (contents.length == 1) {//单个msg
Object object = contents[0];
msg = object == null ? NULL : object.toString();
if (type == JSON) {
msg = formatJson(msg);
}
} else {//传入多个 “msg”
StringBuilder sb = new StringBuilder();
for (int i = 0, len = contents.length; i < len; ++i) {
Object content = contents[i];
sb.append(ARGS)
.append("[")
.append(i)
.append("]")
.append(" = ")
.append(content == null ? NULL : content.toString())
.append(LINE_SEPARATOR);
}
msg = sb.toString();
}
}
//拼接左边边宽
if (isLogBorder) {
StringBuilder sb = new StringBuilder();
String[] lines = msg.split(LINE_SEPARATOR);
for (String line : lines) {
sb.append(LEFT_BORDER).append(line).append(LINE_SEPARATOR);
}
msg = sb.toString();
}
return new String[]{tag, headMsg + msg};
}
/**
* @param json 格式化json字符串
* @return
*/
private static String formatJson(String json) {
// Log.e("TAG", "formatJson: >>" + json);
try {
if (json.startsWith("{")) {
json = new JSONObject(json).toString(4);
} else if (json.startsWith("[")) {
json = new JSONArray(json).toString(4);
}
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
/**
* 打印完整日志
*
* @param type
* @param tag
* @param msg
*/
private static void printLog(int type, String tag, String msg) {
if (isLogBorder)//打印上边框
printBorder(type, tag, true);
//防止 日志长度超限 导致日志信息打印不全
int len = msg.length();
int countOfSub = len / MAX_LEN;
int index = 0;
if (countOfSub > 0) {
String sub;
for (int i = 0; i < countOfSub; i++) {
sub = msg.substring(index, index + MAX_LEN);
printSubLog(type, tag, sub);
index += MAX_LEN;
}
printSubLog(type, tag, msg.substring(index, len));
} else {
printSubLog(type, tag, msg);
}
if (isLogBorder) //打印下边框
printBorder(type, tag, false);
}
/**
* 打印日志内容
*
* @param type
* @param tag
* @param msg
*/
private static void printSubLog(final int type, final String tag, String msg) {
if (isLogBorder)
msg = LEFT_BORDER + msg;
switch (type) {
case V:
Log.v(tag, msg);
break;
case D:
Log.d(tag, msg);
break;
case I:
Log.i(tag, msg);
break;
case W:
Log.w(tag, msg);
break;
case E:
Log.e(tag, msg);
break;
case A:
Log.wtf(tag, msg);
break;
}
}
/**
* 打印日志边框
*
* @param type
* @param tag
* @param isTop
*/
private static void printBorder(int type, String tag, boolean isTop) {
String border = isTop ? TOP_BORDER : BOTTOM_BORDER;
switch (type) {
case V:
Log.v(tag, border);
break;
case D:
Log.d(tag, border);
break;
case I:
Log.i(tag, border);
break;
case W:
Log.w(tag, border);
break;
case E:
Log.e(tag, border);
break;
case A:
Log.wtf(tag, border);
break;
}
}
private static boolean isSpace(String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
}