只要你是程序员就一定熟悉这么一个东西,那就是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 sLoggerTable = new Hashtable();
/**
* 用来记录当前的开发者名称。
*/
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", "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);
}
}
好了现在一个工具类就完成了,下面就一个使用了使用时你只需要通过静态方法获取自己的日志工具就可以了,例如我要打印日志:LogUtil.getKelin().i("测试日志");
现在其他的小伙伴是看不到你的日志信息的,除非在初始化的时候调用四个参数的方法,并将第四个参数filterOtherDeveloperLog设置为false。