只要你是程序員就一定熟悉這麼一個東西,那就是Log。Log是我們日常開發中必不可少的調試工具,如果你是團隊開發中的一員,那麼就一定有過這樣的煩惱,那就是在調試過程中想要看你自己的日誌的時候卻發現其他小夥伴的日誌確異常的多,很快就把你的日誌頂沒了。
這篇帖子將解決你的煩惱。你可以根據需求選擇性的決定是否打印其他開發者的日誌。(好了,下面進入正題。)
首先奉上完整的代碼
public class LogUtil {
/**
* 表示過濾其他開發者日誌。
*/
private static final boolean FILTER_OTHER_DEVELOPER_LOG = true;
/**
* 表示輸出方法信息及位置。
*/
private static final boolean LOG_METHOD_INFO = true;
/**
* 用來存放每個開發者的日誌對象。
*/
private static Hashtable<String, LogUtil> sLoggerTable = new Hashtable<String, LogUtil>();
/**
* 用來記錄當前的開發者名稱。
*/
private static String sCurDeveloperName;
/**
* 用來記錄當前是否是debug模式。
*/
private static boolean sIsDebug;
/**
* 用來記錄是否過濾其他開發者日誌。
*/
private static boolean sIsFilterOtherDeveloperLog;
/**
* 用來記錄是否打印日誌所在的方法的信息。
*/
private static boolean sLogMethodInfo;
/**
* 用來記錄當前的開發者。
*/
private DEVELOPER mDeveloper;
/**
* 初始化函數。需要在Application啓動的時候進行初始化。
*
* @param developerName 開發者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。true表示爲debug模式,false表示爲release模式。
*/
public static void init(String developerName, boolean isDebug) {
init(developerName, isDebug, LOG_METHOD_INFO);
}
/**
* 初始化函數。需要在Application啓動的時候進行初始化。
*
* @param developerName 開發者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @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);
}
/**
* 初始化函數。需要在Application啓動的時候進行初始化。
*
* @param developerName 開發者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。
* @param isDebug 當前是否是debug模式。
* @param logMethodInfo 是否打印日誌所處的方法的信息。
* @param filterOtherDeveloperLog 是否過濾其他開發者日誌,如果爲true表示你講看不到其他開發者的日誌,false則可以。默認爲true。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo, boolean filterOtherDeveloperLog) {
sCurDeveloperName = developerName;
sIsDebug = isDebug;
sLogMethodInfo = logMethodInfo;
sIsFilterOtherDeveloperLog = filterOtherDeveloperLog;
}
/**
* 定義一個開發者枚舉類,改類包含了開發者電腦名稱和開發者的日誌標識。
*/
private enum DEVELOPER {
/**
* 定義開發者-Kelin。
*/
KELIN("kelin", "@kelin@"),
/**
* 定義開發者-張三。
*/
ZHANG_SAN("zhangsan", "@zhang@"),
/**
* 定義開發者-李四。
*/
LISI("lisi", "@lisi@"),
/**
* 定義系統日誌,這個日誌是所有開發者都能看到的。
*/
SYSTEM("system", "@system@");
private String name;
private String value;
/**
* 構造函數。
*
* @param name 開發者的名稱,也是電腦的名稱。
* @param value 開發者的日誌標識,這個會顯示在日誌中。
*/
DEVELOPER(String name, String value) {
this.name = name;
this.value = value;
}
/**
* 獲取開發者名稱。
*/
public String getName() {
return name;
}
/**
* 獲取開發者的日誌標識。
*/
public String getValue() {
return value;
}
}
/**
* 獲取系統級別的日誌工具。
*/
public static LogUtil getSystem() {
return getLogger(DEVELOPER.SYSTEM);
}
/**
* 獲取開發者Kelin的日誌工具。
*/
public static LogUtil getKelin() {
return getLogger(DEVELOPER.KELIN);
}
/**
* 獲取開發者張三的日誌工具。
*/
public static LogUtil getZhangSan() {
return getLogger(DEVELOPER.ZHANG_SAN);
}
/**
* 獲取開發者李四的日誌工具。
*/
public static LogUtil getLiSi() {
return getLogger(DEVELOPER.LISI);
}
/**
* 獲取一個開發者的日誌工具。
*
* @param developer 開發者枚舉對象。
*/
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;
}
/**
* 構造函數。
*
* @param developer 開發者枚舉對象。
*/
private LogUtil(DEVELOPER developer) {
this.mDeveloper = developer;
}
/**
* 日誌是否可以被顯示。
*
* @return 可以顯示返回true,不可以顯示返回false。
*/
private boolean logCanDisplay() {
if (sCurDeveloperName == null) { //如果當前開發者名稱爲null說明沒有調用初始化方法,拋出異常,提示調用。
throw new IllegalStateException("you must call init method int you application!");
}
// 只有系統和本人的日誌才能輸出,如果當前不是Debug模式並且(不過濾其他開發者日誌 或者 當前開發者是日誌打印者 或者 當前日誌是系統級別日誌)
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())) {
//構造方法
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 "";
}
}
下面再來講解一下,因爲這個類你是不能直接拿來用的。
定義枚舉內部類。
/** * 定義一個開發者枚舉類,改類包含了開發者電腦名稱和開發者的日誌標識。 */ private enum DEVELOPER { /** * 定義開發者-Kelin。 */ KELIN("kelin", "@kelin@"), /** * 定義開發者-張三。 */ ZHANG_SAN("zhangsan", "@張三@"), /** * 定義開發者-李四。 */ LISI("lisi", "@李四@"), /** * 定義系統日誌,這個日誌是所有開發者都能看到的。 */ SYSTEM("system", "@system@"); private String name; private String value; /** * 構造函數。 * @param name 開發者的名稱,也是電腦的名稱。 * @param value 開發者的日誌標識,這個會顯示在日誌中。 */ DEVELOPER(String name, String value) { this.name = name; this.value = value; } /** * 獲取開發者名稱。 */ public String getName() { return name; } /** * 獲取開發者的日誌標識。 */ public String getValue() { return value; } }
上面的DEVELOPER枚舉類是定義了開發者,也就是說你的團隊中有幾個人就需要定義幾個枚舉,DEVELOPER枚舉類中有兩個成員變量,分別爲name和value(當然,變量名你可以隨便定義。),name爲開發者的電腦名稱,而value爲開發者在日誌中的標識,你可以把它看做爲暱稱,它將顯示在日誌中。上面我是爲我自己和Team中的張三和李四分別創建了枚舉。除此之外還有一個SYSTEM枚舉,這個是系統級別的日誌,如果你希望某個日誌信息可以被所有小夥伴看到則需要打印此級別的日誌。
定義獲取每個成員的LogUtil對象的靜態方法。
/** * 獲取系統級別的日誌工具。 */ public static LogUtil getSystem() { return getLogger(DEVELOPER.SYSTEM); } /** * 獲取開發者Kelin的日誌工具。 */ public static LogUtil getKelin() { return getLogger(DEVELOPER.KELIN); } /** * 獲取開發者張三的日誌工具。 */ public static LogUtil getZhangSan() { return getLogger(DEVELOPER.ZHANG_SAN); } /** * 獲取開發者李四的日誌工具。 */ public static LogUtil getLiSi() { return getLogger(DEVELOPER.LISI); } /** * 獲取一個開發者的日誌工具。 * @param developer 開發者枚舉對象。 */ 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; }
這裏的代碼就不解釋了,就是提供靜態方法讓小夥伴們獲取自己的日誌工具類。
- 別忘了將構造方法私有,因爲既然是工具類就不應該讓別人new出來用。而是通過靜態方法獲取。
最後一點,有幾個重載的初始化方法別忘記了。
/** * 初始化函數。需要在Application啓動的時候進行初始化。 * * @param developerName 開發者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。 * @param isDebug 當前是否是debug模式。true表示爲debug模式,false表示爲release模式。 */ public static void init(String developerName, boolean isDebug) { init(developerName, isDebug, LOG_METHOD_INFO); } /** * 初始化函數。需要在Application啓動的時候進行初始化。 * * @param developerName 開發者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。 * @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); } /** * 初始化函數。需要在Application啓動的時候進行初始化。 * * @param developerName 開發者名稱,也是電腦名稱。如果你的電腦沒有設置過名稱請設置一下。 * @param isDebug 當前是否是debug模式。 * @param logMethodInfo 是否打印日誌所處的方法的信息。 * @param filterOtherDeveloperLog 是否過濾其他開發者日誌,如果爲true表示你講看不到其他開發者的日誌,false則可以。默認爲true。 */ public static void init(String developerName, boolean isDebug, boolean logMethodInfo, boolean filterOtherDeveloperLog) { sCurDeveloperName = developerName; sIsDebug = isDebug; sLogMethodInfo = logMethodInfo; sIsFilterOtherDeveloperLog = filterOtherDeveloperLog; }
這裏也沒有什麼好解釋的,相信我的註釋還是比較詳細和簡單易懂的。這三個只需要選擇一個適合你的在Application的onCreate方法中調用一次即可。
重點是這個電腦名稱應該怎麼給從哪裏獲取這纔是關鍵。(有的人想說,直接看下電腦名稱然後寫死不就好了嗎?如果是這樣就不用提供方法在初始化的時候傳入了(*^_^* ) 。)
獲取電腦名稱並調用初始化方法。
獲取電腦名稱要在App的Gradle中的buildTypes{}下增加如下代碼:
//因爲BuildConfig.DEBUG有時候不是那麼好用,所以自己寫一個。
buildConfigField "boolean", "IS_DEBUG", "false"
//下面這句是獲取當前的電腦名稱。併爲BuildConfig增加一個DEVELOPER_NAME字符串字段。
buildConfigField "String", "DEVELOPER_NAME", "\"${System.properties['user.name']}\""
上線的這兩句要分別加在release和debug節點下面。下面是完整的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", "false"
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);
}
}
好了現在一個工具類就完成了,下面就一個使用了使用時你只需要通過靜態方法獲取自己的日誌工具就可以了,例如我要打印日誌:LogUtil.getKelin().i("測試日誌");
現在其他的小夥伴是看不到你的日誌信息的,除非在初始化的時候調用四個參數的方法,並將第四個參數filterOtherDeveloperLog設置爲false。