只要你是程序員就一定熟悉這么一個(gè)東西掐场,那就是Log往扔。Log是我們?nèi)粘i_發(fā)中必不可少的調(diào)試工具贩猎,如果你是團(tuán)隊(duì)開發(fā)中的一員,那么就一定有過這樣的煩惱瓤球,那就是在調(diào)試過程中想要看你自己的日志的時(shí)候卻發(fā)現(xiàn)其他小伙伴的日志確異常的多融欧,很快就把你的日志頂沒了。
這篇帖子將解決你的煩惱卦羡。你可以根據(jù)需求選擇性的決定是否打印其他開發(fā)者的日志噪馏。(好了,下面進(jìn)入正題绿饵。)
首先奉上完整的代碼
public class LogUtil {
/**
* 表示過濾其他開發(fā)者日志欠肾。
*/
private static final boolean FILTER_OTHER_DEVELOPER_LOG = true;
/**
* 表示輸出方法信息及位置。
*/
private static final boolean LOG_METHOD_INFO = true;
/**
* 用來存放每個(gè)開發(fā)者的日志對象拟赊。
*/
private static Hashtable<String, LogUtil> sLoggerTable = new Hashtable<String, LogUtil>();
/**
* 用來記錄當(dāng)前的開發(fā)者名稱刺桃。
*/
private static String sCurDeveloperName;
/**
* 用來記錄當(dāng)前是否是debug模式。
*/
private static boolean sIsDebug;
/**
* 用來記錄是否過濾其他開發(fā)者日志吸祟。
*/
private static boolean sIsFilterOtherDeveloperLog;
/**
* 用來記錄是否打印日志所在的方法的信息瑟慈。
*/
private static boolean sLogMethodInfo;
/**
* 用來記錄當(dāng)前的開發(fā)者。
*/
private DEVELOPER mDeveloper;
/**
* 初始化函數(shù)屋匕。需要在Application啟動(dòng)的時(shí)候進(jìn)行初始化葛碧。
*
* @param developerName 開發(fā)者名稱,也是電腦名稱过吻。如果你的電腦沒有設(shè)置過名稱請?jiān)O(shè)置一下进泼。
* @param isDebug 當(dāng)前是否是debug模式。true表示為debug模式纤虽,false表示為release模式乳绕。
*/
public static void init(String developerName, boolean isDebug) {
init(developerName, isDebug, LOG_METHOD_INFO);
}
/**
* 初始化函數(shù)。需要在Application啟動(dòng)的時(shí)候進(jìn)行初始化逼纸。
*
* @param developerName 開發(fā)者名稱洋措,也是電腦名稱。如果你的電腦沒有設(shè)置過名稱請?jiān)O(shè)置一下杰刽。
* @param isDebug 當(dāng)前是否是debug模式呻纹。
* @param logMethodInfo 是否打印日志所處的方法的信息。true表示打印专缠,false表示不打印。默認(rèn)為true淑仆。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo) {
init(developerName, isDebug, logMethodInfo, FILTER_OTHER_DEVELOPER_LOG);
}
/**
* 初始化函數(shù)涝婉。需要在Application啟動(dòng)的時(shí)候進(jìn)行初始化。
*
* @param developerName 開發(fā)者名稱蔗怠,也是電腦名稱墩弯。如果你的電腦沒有設(shè)置過名稱請?jiān)O(shè)置一下吩跋。
* @param isDebug 當(dāng)前是否是debug模式。
* @param logMethodInfo 是否打印日志所處的方法的信息渔工。
* @param filterOtherDeveloperLog 是否過濾其他開發(fā)者日志锌钮,如果為true表示你講看不到其他開發(fā)者的日志,false則可以引矩。默認(rèn)為true梁丘。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo, boolean filterOtherDeveloperLog) {
sCurDeveloperName = developerName;
sIsDebug = isDebug;
sLogMethodInfo = logMethodInfo;
sIsFilterOtherDeveloperLog = filterOtherDeveloperLog;
}
/**
* 定義一個(gè)開發(fā)者枚舉類,改類包含了開發(fā)者電腦名稱和開發(fā)者的日志標(biāo)識旺韭。
*/
private enum DEVELOPER {
/**
* 定義開發(fā)者-Kelin氛谜。
*/
KELIN("kelin", "@kelin@"),
/**
* 定義開發(fā)者-張三。
*/
ZHANG_SAN("zhangsan", "@zhang@"),
/**
* 定義開發(fā)者-李四区端。
*/
LISI("lisi", "@lisi@"),
/**
* 定義系統(tǒng)日志值漫,這個(gè)日志是所有開發(fā)者都能看到的。
*/
SYSTEM("system", "@system@");
private String name;
private String value;
/**
* 構(gòu)造函數(shù)织盼。
*
* @param name 開發(fā)者的名稱杨何,也是電腦的名稱。
* @param value 開發(fā)者的日志標(biāo)識沥邻,這個(gè)會(huì)顯示在日志中危虱。
*/
DEVELOPER(String name, String value) {
this.name = name;
this.value = value;
}
/**
* 獲取開發(fā)者名稱。
*/
public String getName() {
return name;
}
/**
* 獲取開發(fā)者的日志標(biāo)識谋国。
*/
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);
}
/**
* 獲取一個(gè)開發(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) { //如果當(dāng)前開發(fā)者名稱為null說明沒有調(diào)用初始化方法二鳄,拋出異常赴涵,提示調(diào)用。
throw new IllegalStateException("you must call init method int you application!");
}
// 只有系統(tǒng)和本人的日志才能輸出订讼,如果當(dāng)前不是Debug模式并且(不過濾其他開發(fā)者日志 或者 當(dāng)前開發(fā)者是日志打印者 或者 當(dāng)前日志是系統(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);
}
}
}
/**
* 獲取當(dāng)前方法的詳細(xì)信息
* 具體到方法名程拭、方法行定踱,方法所在類的文件名
*/
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 "";
}
}
下面再來講解一下,因?yàn)檫@個(gè)類你是不能直接拿來用的恃鞋。
- 定義枚舉內(nèi)部類崖媚。
/**
* 定義一個(gè)開發(fā)者枚舉類,改類包含了開發(fā)者電腦名稱和開發(fā)者的日志標(biāo)識山宾。
*/
private enum DEVELOPER {
/**
* 定義開發(fā)者-Kelin至扰。
*/
KELIN("kelin", "@kelin@"),
/**
* 定義開發(fā)者-張三。
*/
ZHANG_SAN("zhangsan", "@張三@"),
/**
* 定義開發(fā)者-李四资锰。
*/
LISI("lisi", "@李四@"),
/**
* 定義系統(tǒng)日志敢课,這個(gè)日志是所有開發(fā)者都能看到的。
*/
SYSTEM("system", "@system@");
private String name;
private String value;
/**
* 構(gòu)造函數(shù)绷杜。
* @param name 開發(fā)者的名稱直秆,也是電腦的名稱。
* @param value 開發(fā)者的日志標(biāo)識鞭盟,這個(gè)會(huì)顯示在日志中圾结。
*/
DEVELOPER(String name, String value) {
this.name = name;
this.value = value;
}
/**
* 獲取開發(fā)者名稱。
*/
public String getName() {
return name;
}
/**
* 獲取開發(fā)者的日志標(biāo)識齿诉。
*/
public String getValue() {
return value;
}
}
上面的DEVELOPER枚舉類是定義了開發(fā)者筝野,也就是說你的團(tuán)隊(duì)中有幾個(gè)人就需要定義幾個(gè)枚舉,DEVELOPER枚舉類中有兩個(gè)成員變量粤剧,分別為name和value(當(dāng)然歇竟,變量名你可以隨便定義。)抵恋,name為開發(fā)者的電腦名稱焕议,而value為開發(fā)者在日志中的標(biāo)識,你可以把它看做為昵稱弧关,它將顯示在日志中盅安。上面我是為我自己和Team中的張三和李四分別創(chuàng)建了枚舉。除此之外還有一個(gè)SYSTEM枚舉世囊,這個(gè)是系統(tǒng)級別的日志别瞭,如果你希望某個(gè)日志信息可以被所有小伙伴看到則需要打印此級別的日志。
- 定義獲取每個(gè)成員的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);
}
/**
* 獲取一個(gè)開發(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)造方法私有弃秆,因?yàn)榧热皇枪ぞ哳惥筒粦?yīng)該讓別人new出來用届惋。而是通過靜態(tài)方法獲取。
- 最后一點(diǎn)菠赚,有幾個(gè)重載的初始化方法別忘記了脑豹。
/**
* 初始化函數(shù)。需要在Application啟動(dòng)的時(shí)候進(jìn)行初始化衡查。
*
* @param developerName 開發(fā)者名稱瘩欺,也是電腦名稱。如果你的電腦沒有設(shè)置過名稱請?jiān)O(shè)置一下拌牲。
* @param isDebug 當(dāng)前是否是debug模式俱饿。true表示為debug模式,false表示為release模式塌忽。
*/
public static void init(String developerName, boolean isDebug) {
init(developerName, isDebug, LOG_METHOD_INFO);
}
/**
* 初始化函數(shù)拍埠。需要在Application啟動(dòng)的時(shí)候進(jìn)行初始化。
*
* @param developerName 開發(fā)者名稱土居,也是電腦名稱枣购。如果你的電腦沒有設(shè)置過名稱請?jiān)O(shè)置一下。
* @param isDebug 當(dāng)前是否是debug模式擦耀。
* @param logMethodInfo 是否打印日志所處的方法的信息棉圈。true表示打印,false表示不打印埂奈。默認(rèn)為true迄损。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo) {
init(developerName, isDebug, logMethodInfo, FILTER_OTHER_DEVELOPER_LOG);
}
/**
* 初始化函數(shù)。需要在Application啟動(dòng)的時(shí)候進(jìn)行初始化账磺。
*
* @param developerName 開發(fā)者名稱芹敌,也是電腦名稱。如果你的電腦沒有設(shè)置過名稱請?jiān)O(shè)置一下垮抗。
* @param isDebug 當(dāng)前是否是debug模式氏捞。
* @param logMethodInfo 是否打印日志所處的方法的信息。
* @param filterOtherDeveloperLog 是否過濾其他開發(fā)者日志冒版,如果為true表示你講看不到其他開發(fā)者的日志液茎,false則可以。默認(rèn)為true。
*/
public static void init(String developerName, boolean isDebug, boolean logMethodInfo, boolean filterOtherDeveloperLog) {
sCurDeveloperName = developerName;
sIsDebug = isDebug;
sLogMethodInfo = logMethodInfo;
sIsFilterOtherDeveloperLog = filterOtherDeveloperLog;
}
這里也沒有什么好解釋的捆等,相信我的注釋還是比較詳細(xì)和簡單易懂的滞造。這三個(gè)只需要選擇一個(gè)適合你的在Application的onCreate方法中調(diào)用一次即可。
重點(diǎn)是這個(gè)電腦名稱應(yīng)該怎么給從哪里獲取這才是關(guān)鍵栋烤。(有的人想說谒养,直接看下電腦名稱然后寫死不就好了嗎?如果是這樣就不用提供方法在初始化的時(shí)候傳入了(*_* ) 明郭。)
獲取電腦名稱并調(diào)用初始化方法买窟。
獲取電腦名稱要在App的Gradle中的buildTypes{}下增加如下代碼:
//因?yàn)锽uildConfig.DEBUG有時(shí)候不是那么好用,所以自己寫一個(gè)薯定。
buildConfigField "boolean", "IS_DEBUG", "false"
//下面這句是獲取當(dāng)前的電腦名稱始绍。并為BuildConfig增加一個(gè)DEVELOPER_NAME字符串字段。
buildConfigField "String", "DEVELOPER_NAME", "\"${System.properties['user.name']}\""
上線的這兩句要分別加在release和debug節(jié)點(diǎn)下面话侄。下面是完整的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)在一個(gè)工具類就完成了满葛,下面就一個(gè)使用了使用時(shí)你只需要通過靜態(tài)方法獲取自己的日志工具就可以了径簿,例如我要打印日志:LogUtil.getKelin().i("測試日志");
現(xiàn)在其他的小伙伴是看不到你的日志信息的,除非在初始化的時(shí)候調(diào)用四個(gè)參數(shù)的方法嘀韧,并將第四個(gè)參數(shù)filterOtherDeveloperLog設(shè)置為false篇亭。