只要你是程序員就一定熟悉這么一個東西,那就是Log。Log是我們?nèi)粘i_發(fā)中必不可少的調(diào)試工具,如果你是團隊開發(fā)中的一員,那么就一定有過這樣的煩惱,那就是在調(diào)試過程中想要看你自己的日志的時候卻發(fā)現(xiàn)其他小伙伴的日志確異常的多,很快就把你的日志頂沒了。
這篇帖子將解決你的煩惱。你可以根據(jù)需求選擇性的決定是否打印其他開發(fā)者的日志。(好了,下面進入正題。)
首先奉上完整的代碼
public class LogUtil {
/**
* 表示過濾其他開發(fā)者日志。
*/
private static final boolean FILTER_OTHER_DEVELOPER_LOG = true;
/**
* 表示輸出方法信息及位置。
*/
private static final boolean LOG_METHOD_INFO = true;
/**
* 用來存放每個開發(fā)者的日志對象。
*/
private static Hashtable<String, LogUtil> sLoggerTable = new Hashtable<String, LogUtil>();
/**
* 用來記錄當前的開發(fā)者名稱。
*/
private static String sCurDeveloperName;
/**
* 用來記錄當前是否是debug模式。
*/
private static boolean sIsDebug;
/**
* 用來記錄是否過濾其他開發(fā)者日志。
*/
private static boolean sIsFilterOtherDeveloperLog;
/**
* 用來記錄是否打印日志所在的方法的信息。
*/
private static boolean sLogMethodInfo;
/**
* 用來記錄當前的開發(fā)者。
*/
private DEVELOPER mDeveloper;
/**
* 初始化函數(shù)。需要在Application啟動的時候進行初始化。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。true表示為debug模式,false表示為release模式。
*/
public static void init(String developerName, boolean isDebug) {
init(developerName, isDebug, LOG_METHOD_INFO);
}
/**
* 初始化函數(shù)。需要在Application啟動的時候進行初始化。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。
* @param logMethodInfo 是否打印日志所處的方法的信息。true表示打印,false表示不打印。默認為true。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo) {
init(developerName, isDebug, logMethodInfo, FILTER_OTHER_DEVELOPER_LOG);
}
/**
* 初始化函數(shù)。需要在Application啟動的時候進行初始化。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。
* @param logMethodInfo 是否打印日志所處的方法的信息。
* @param filterOtherDeveloperLog 是否過濾其他開發(fā)者日志,如果為true表示你講看不到其他開發(fā)者的日志,false則可以。默認為true。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo, boolean filterOtherDeveloperLog) {
sCurDeveloperName = developerName;
sIsDebug = isDebug;
sLogMethodInfo = logMethodInfo;
sIsFilterOtherDeveloperLog = filterOtherDeveloperLog;
}
/**
* 定義一個開發(fā)者枚舉類,改類包含了開發(fā)者電腦名稱和開發(fā)者的日志標識。
*/
private enum DEVELOPER {
/**
* 定義開發(fā)者-Kelin。
*/
KELIN("kelin", "@kelin@"),
/**
* 定義開發(fā)者-張三。
*/
ZHANG_SAN("zhangsan", "@zhang@"),
/**
* 定義開發(fā)者-李四。
*/
LISI("lisi", "@lisi@"),
/**
* 定義系統(tǒng)日志,這個日志是所有開發(fā)者都能看到的。
*/
SYSTEM("system", "@system@");
private String name;
private String value;
/**
* 構(gòu)造函數(shù)。
*
* @param name 開發(fā)者的名稱,也是電腦的名稱。
* @param value 開發(fā)者的日志標識,這個會顯示在日志中。
*/
DEVELOPER(String name, String value) {
this.name = name;
this.value = value;
}
/**
* 獲取開發(fā)者名稱。
*/
public String getName() {
return name;
}
/**
* 獲取開發(fā)者的日志標識。
*/
public String getValue() {
return value;
}
}
/**
* 獲取系統(tǒng)級別的日志工具。
*/
public static LogUtil getSystem() {
return getLogger(DEVELOPER.SYSTEM);
}
/**
* 獲取開發(fā)者Kelin的日志工具。
*/
public static LogUtil getKelin() {
return getLogger(DEVELOPER.KELIN);
}
/**
* 獲取開發(fā)者張三的日志工具。
*/
public static LogUtil getZhangSan() {
return getLogger(DEVELOPER.ZHANG_SAN);
}
/**
* 獲取開發(fā)者李四的日志工具。
*/
public static LogUtil getLiSi() {
return getLogger(DEVELOPER.LISI);
}
/**
* 獲取一個開發(fā)者的日志工具。
*
* @param developer 開發(fā)者枚舉對象。
*/
private static LogUtil getLogger(DEVELOPER developer) {
LogUtil logger = sLoggerTable.get(developer.getName());
if (logger == null) {
logger = new LogUtil(developer);
sLoggerTable.put(developer.getName(), logger);
}
return logger;
}
/**
* 構(gòu)造函數(shù)。
*
* @param developer 開發(fā)者枚舉對象。
*/
private LogUtil(DEVELOPER developer) {
this.mDeveloper = developer;
}
/**
* 日志是否可以被顯示。
*
* @return 可以顯示返回true,不可以顯示返回false。
*/
private boolean logCanDisplay() {
if (sCurDeveloperName == null) { //如果當前開發(fā)者名稱為null說明沒有調(diào)用初始化方法,拋出異常,提示調(diào)用。
throw new IllegalStateException("you must call init method int you application!");
}
// 只有系統(tǒng)和本人的日志才能輸出,如果當前不是Debug模式并且(不過濾其他開發(fā)者日志 或者 當前開發(fā)者是日志打印者 或者 當前日志是系統(tǒng)級別日志)
return sIsDebug && (!sIsFilterOtherDeveloperLog || sCurDeveloperName.equalsIgnoreCase(mDeveloper.getName()) || DEVELOPER.SYSTEM.getName().equals(mDeveloper.getName()));
}
/**
* 格式化Tag。
*
* @param tag 要格式化的Tag。
* @return 返回格式化后的Tag。
*/
@NonNull
private String formatTag(String tag) {
return String.format("%s[%s]:", TextUtils.isEmpty(tag) ? "" : tag, mDeveloper.getValue());
}
/**
* 格式化Msg。
*
* @param msg 要格式化的Msg。
* @return 返回格式化后的Msg。
*/
@NonNull
private String formatMsg(@NonNull String msg) {
return sLogMethodInfo ? String.format("%s ==> %s", msg, getFunctionName()) : msg;
}
public void i(@NonNull String msg) {
i(null, msg);
}
public void i(String tag, @NonNull String msg) {
if (logCanDisplay() && !TextUtils.isEmpty(msg)) {
Log.i(formatTag(tag), formatMsg(msg));
}
}
public void d(@NonNull String msg) {
d(null, msg);
}
public void d(String tag, @NonNull String msg) {
if (logCanDisplay() && !TextUtils.isEmpty(msg)) {
Log.d(formatTag(tag), formatMsg(msg));
}
}
public void e(@NonNull String msg) {
e(null, msg);
}
public void e(String tag, @NonNull String msg) {
e(tag, msg, null);
}
public void e(String tag, @NonNull String msg, Throwable e) {
if (logCanDisplay() && !TextUtils.isEmpty(msg)) {
if (e == null) {
Log.e(formatTag(tag), formatMsg(msg));
} else {
Log.e(formatTag(tag), formatMsg(msg), e);
}
}
}
/**
* 獲取當前方法的詳細信息
* 具體到方法名、方法行,方法所在類的文件名
*/
private String getFunctionName() {
StackTraceElement[] sts = Thread.currentThread().getStackTrace();
if (sts == null) {
return "";
}
for (StackTraceElement st : sts) {
if (st.isNativeMethod()) {
//本地方法native jni
continue;
}
if (st.getClassName().equals(Thread.class.getName())) {
//線程
continue;
}
if (st.getClassName().equals(this.getClass().getName())) {
//構(gòu)造方法
continue;
}
String[] split = st.getClassName().split("\\.");
String className = split.length == 0 ? st.getClassName() : split[split.length - 1];
return "[Thread: " + Thread.currentThread().getName() + " Class: "
+ className + " Line:" + st.getLineNumber() + " Method: "
+ st.getMethodName() + "]";
}
return "";
}
}
下面再來講解一下,因為這個類你是不能直接拿來用的。
- 定義枚舉內(nèi)部類。
/**
* 定義一個開發(fā)者枚舉類,改類包含了開發(fā)者電腦名稱和開發(fā)者的日志標識。
*/
private enum DEVELOPER {
/**
* 定義開發(fā)者-Kelin。
*/
KELIN("kelin", "@kelin@"),
/**
* 定義開發(fā)者-張三。
*/
ZHANG_SAN("zhangsan", "@張三@"),
/**
* 定義開發(fā)者-李四。
*/
LISI("lisi", "@李四@"),
/**
* 定義系統(tǒng)日志,這個日志是所有開發(fā)者都能看到的。
*/
SYSTEM("system", "@system@");
private String name;
private String value;
/**
* 構(gòu)造函數(shù)。
* @param name 開發(fā)者的名稱,也是電腦的名稱。
* @param value 開發(fā)者的日志標識,這個會顯示在日志中。
*/
DEVELOPER(String name, String value) {
this.name = name;
this.value = value;
}
/**
* 獲取開發(fā)者名稱。
*/
public String getName() {
return name;
}
/**
* 獲取開發(fā)者的日志標識。
*/
public String getValue() {
return value;
}
}
上面的DEVELOPER枚舉類是定義了開發(fā)者,也就是說你的團隊中有幾個人就需要定義幾個枚舉,DEVELOPER枚舉類中有兩個成員變量,分別為name和value(當然,變量名你可以隨便定義。),name為開發(fā)者的電腦名稱,而value為開發(fā)者在日志中的標識,你可以把它看做為昵稱,它將顯示在日志中。上面我是為我自己和Team中的張三和李四分別創(chuàng)建了枚舉。除此之外還有一個SYSTEM枚舉,這個是系統(tǒng)級別的日志,如果你希望某個日志信息可以被所有小伙伴看到則需要打印此級別的日志。
- 定義獲取每個成員的LogUtil對象的靜態(tài)方法。
/**
* 獲取系統(tǒng)級別的日志工具。
*/
public static LogUtil getSystem() {
return getLogger(DEVELOPER.SYSTEM);
}
/**
* 獲取開發(fā)者Kelin的日志工具。
*/
public static LogUtil getKelin() {
return getLogger(DEVELOPER.KELIN);
}
/**
* 獲取開發(fā)者張三的日志工具。
*/
public static LogUtil getZhangSan() {
return getLogger(DEVELOPER.ZHANG_SAN);
}
/**
* 獲取開發(fā)者李四的日志工具。
*/
public static LogUtil getLiSi() {
return getLogger(DEVELOPER.LISI);
}
/**
* 獲取一個開發(fā)者的日志工具。
* @param developer 開發(fā)者枚舉對象。
*/
private static LogUtil getLogger(DEVELOPER developer) {
LogUtil logger = sLoggerTable.get(developer.getName());
if (logger == null) {
logger = new LogUtil(developer);
sLoggerTable.put(developer.getName(), logger);
}
return logger;
}
這里的代碼就不解釋了,就是提供靜態(tài)方法讓小伙伴們獲取自己的日志工具類。
- 別忘了將構(gòu)造方法私有,因為既然是工具類就不應該讓別人new出來用。而是通過靜態(tài)方法獲取。
- 最后一點,有幾個重載的初始化方法別忘記了。
/**
* 初始化函數(shù)。需要在Application啟動的時候進行初始化。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。true表示為debug模式,false表示為release模式。
*/
public static void init(String developerName, boolean isDebug) {
init(developerName, isDebug, LOG_METHOD_INFO);
}
/**
* 初始化函數(shù)。需要在Application啟動的時候進行初始化。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。
* @param logMethodInfo 是否打印日志所處的方法的信息。true表示打印,false表示不打印。默認為true。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo) {
init(developerName, isDebug, logMethodInfo, FILTER_OTHER_DEVELOPER_LOG);
}
/**
* 初始化函數(shù)。需要在Application啟動的時候進行初始化。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。
* @param logMethodInfo 是否打印日志所處的方法的信息。
* @param filterOtherDeveloperLog 是否過濾其他開發(fā)者日志,如果為true表示你講看不到其他開發(fā)者的日志,false則可以。默認為true。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo, boolean filterOtherDeveloperLog) {
sCurDeveloperName = developerName;
sIsDebug = isDebug;
sLogMethodInfo = logMethodInfo;
sIsFilterOtherDeveloperLog = filterOtherDeveloperLog;
}
這里也沒有什么好解釋的,相信我的注釋還是比較詳細和簡單易懂的。這三個只需要選擇一個適合你的在Application的onCreate方法中調(diào)用一次即可。
重點是這個電腦名稱應該怎么給從哪里獲取這才是關鍵。(有的人想說,直接看下電腦名稱然后寫死不就好了嗎?如果是這樣就不用提供方法在初始化的時候傳入了(*_* ) 。)
獲取電腦名稱并調(diào)用初始化方法。
獲取電腦名稱要在App的Gradle中的buildTypes{}下增加如下代碼:
//因為BuildConfig.DEBUG有時候不是那么好用,所以自己寫一個。
buildConfigField "boolean", "IS_DEBUG", "false"
//下面這句是獲取當前的電腦名稱。并為BuildConfig增加一個DEVELOPER_NAME字符串字段。
buildConfigField "String", "DEVELOPER_NAME", "\"${System.properties['user.name']}\""
上線的這兩句要分別加在release和debug節(jié)點下面。下面是完整的buildTypes。
buildTypes {
release {
buildConfigField "boolean", "IS_DEBUG", "false"
buildConfigField "String", "DEVELOPER_NAME", "\"${System.properties['user.name']}\""
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//…… 省略部分代碼
}
debug {
buildConfigField "boolean", "IS_DEBUG", "true"
buildConfigField "String", "DEVELOPER_NAME", "\"${System.properties['user.name']}\""
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//…… 省略部分代碼
}
}
完成上面的你就可以在Application中初始化了(別忘了在清單文件中使用你自定義的Application),代碼如下:
public class App extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
LogUtil.init(BuildConfig.DEVELOPER_NAME, BuildConfig.IS_DEBUG);
}
}
好了現(xiàn)在一個工具類就完成了,下面就一個使用了使用時你只需要通過靜態(tài)方法獲取自己的日志工具就可以了,例如我要打印日志:LogUtil.getKelin().i("測試日志");
現(xiàn)在其他的小伙伴是看不到你的日志信息的,除非在初始化的時候調(diào)用四個參數(shù)的方法,并將第四個參數(shù)filterOtherDeveloperLog設置為false。