最近项目中有使用Uber的zap(Go语言生态中一种高效打印Log的实用库,久经考验)打印log,用的顺手,于是借鉴其中的设计思想,在Android环境下封装Log类提供相似功能。
分离消息与数据域,避免字符串拼接和效率低的字符串格式化
// file: MainActivity.java
@Override
protected void onResume() {
super.onResume();
Logus.d("just a message");
Logus.e("a field message", "a", 1);
Logus.w("3 fields but see 2", "a", 1, "b", true, "c");
Logus.V("Main", "with prefix just a message");
Logus.E("Main", "with prefix 2 fields", "a", 10, "b", false);
Logus.E("Main", "with prefix 3 fields but log 2", "a", 10, "b", true, "c");
Logus.setStackTrace(false);
Logus.E("Main", "no stack trace with prefix");
}
// 输出:
04-19 22:12:56.209 3256-3256/net.gility.note D/Logus: MainActivity.java(425)#onResume > just a message
04-19 22:12:56.210 3256-3256/net.gility.note E/Logus: MainActivity.java(426)#onResume > a field message { a=1; }
04-19 22:12:56.210 3256-3256/net.gility.note W/Logus: MainActivity.java(427)#onResume > 3 fields but see 2 { a=1; b=true; }
04-19 22:12:56.211 3256-3256/net.gility.note V/Logus: MainActivity.java(428)#onResume Main> with prefix just a message
04-19 22:12:56.212 3256-3256/net.gility.note E/Logus: MainActivity.java(429)#onResume Main> with prefix 2 fields { a=10; b=false; }
04-19 22:12:56.213 3256-3256/net.gility.note E/Logus: MainActivity.java(430)#onResume Main> with prefix 3 fields but log 2 { a=10; b=true; }
04-19 22:12:56.213 3256-3256/net.gility.note D/Logus: > no stack trace no prefix
04-19 22:12:56.213 3256-3256/net.gility.note E/Logus: Main> no stack trace with prefix
public final class Logus {
// 数据域长度调整掩码,用于计算数据域长度;aInt & FIXED_LENGTH_MASK 相当于 aInt % 2 == 0 ? aInt : aInt - 1;
private static final int FIXED_LENGTH_MASK = ~1;
private static String sPreTag = "Logus"; // 预设TAG
private static String sPrePrefix = ""; // 预设消息前缀
private static boolean sIsStackTrace = true; // 是否打印StackTrace Info
/**
* 设置TAG
*
* @param tag
*/
public static void setTag(final String tag) {
if (!TextUtils.isEmpty(tag)) {
sPreTag = tag;
}
}
/**
* 设置消息前缀
*
* @param prefix
*/
public static void setPrefix(final String prefix) {
if (!TextUtils.isEmpty(prefix)) {
sPrePrefix = prefix;
} else {
sPrePrefix = "";
}
}
/**
* 设置是否打印StackTrace信息 (显示文件名(行数)#函数名:)
*
* @param isStackTrace true/false
*/
public static void setStackTrace(boolean isStackTrace) {
sIsStackTrace = isStackTrace;
}
/**
* Verbose level log
*
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void v(final String message, Object... fields) {
printMsg(Log.VERBOSE, sPrePrefix, message, fields);
}
/**
* Debug level log print
*
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void d(final String message, Object... fields) {
printMsg(Log.DEBUG, sPrePrefix, message, fields);
}
/**
* Info level log print
*
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void i(final String message, Object... fields) {
printMsg(Log.INFO, sPrePrefix, message, fields);
}
/**
* Warning level log print
*
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void w(final String message, Object... fields) {
printMsg(Log.WARN, sPrePrefix, message, fields);
}
/**
* Error level log print
*
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void e(final String message, Object... fields) {
printMsg(Log.ERROR, sPrePrefix, message, fields);
}
/**
* Verbose level log
*
* @param prefix 消息前缀
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void V(final String prefix, final String message, Object... fields) {
printMsg(Log.VERBOSE, prefix, message, fields);
}
/**
* Debug level log print
*
* @param prefix 消息前缀
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void D(final String prefix, final String message, Object... fields) {
printMsg(Log.DEBUG, prefix, message, fields);
}
/**
* Info level log print
*
* @param prefix 消息前缀
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void I(final String prefix, final String message, Object... fields) {
printMsg(Log.INFO, prefix, message, fields);
}
/**
* Warning level log print
*
* @param prefix 消息前缀
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void W(final String prefix, final String message, Object... fields) {
printMsg(Log.WARN, prefix, message, fields);
}
/**
* Error level log print
*
* @param prefix 消息前缀
* @param message 消息
* @param fields 数据域,键-值连续的数据对,必须确保fields的长度为偶数,否则最后一个数据将不会打印
*/
public static void E(final String prefix, final String message, Object... fields) {
printMsg(Log.ERROR, prefix, message, fields);
}
/**
* 打印log
*
* @param level log级别
* @param prefix 前缀
* @param message 消息体(必须的)
* @param fields 数据域
*/
private static void printMsg(final int level, final String prefix, final String message, Object... fields) {
StringBuilder sb = new StringBuilder();
// 添加栈信息(文件及函数信息)
if (sIsStackTrace) {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
if (elements != null && elements.length > 4) {
sb.append(elements[4].getFileName());
sb.append("(");
sb.append(elements[4].getLineNumber());
sb.append(")#");
sb.append(elements[4].getMethodName());
sb.append(" ");
}
}
sb.append(prefix);
sb.append("> ");
sb.append(message);
if (fields.length > 1) {
sb.append(" { ");
for (int i = 0, j = fields.length & FIXED_LENGTH_MASK; i < j; ) {
sb.append(String.valueOf(fields[i++]));
sb.append("=");
sb.append(String.valueOf(fields[i++]));
sb.append("; ");
}
sb.append("}");
}
switch (level) {
case Log.VERBOSE:
Log.v(sPreTag, sb.toString());
break;
case Log.DEBUG:
Log.d(sPreTag, sb.toString());
break;
case Log.INFO:
Log.i(sPreTag, sb.toString());
break;
case Log.WARN:
Log.w(sPreTag, sb.toString());
break;
case Log.ERROR:
Log.e(sPreTag, sb.toString());
break;
default:
break;
}
}
}