简单介绍
android自带的Log类只能打印出tag标签和日志内容,有如下缺点:
- 过滤起来不方便,往往单单看控制台的日志信息,难以定位日志所在源代码处
- 无法获取执行代码所在的线程。
- 无法在release包中统一控制日志开关
LogHelper
通过对系统Log类进行代理封装,相比系统Log类有如下优势:
- 自动打印出所在方法、类,精确定位源码处
- 打印线程信息
- 提供字符串格式化参数的功能
- 支持release不打印log(通过adb指令,控制release包中是否打印出指定级别日志,既保证了release包日志信息导致的安全性问题,又便于开发者调试)
使用场景
任何android项目,尤其是独立的库或者SDK项目中,可通过类中的TAG统一过滤出库或SDK相关的日志,避免与主工程的日志混淆
使用示例
//其他级别的日志调用方式同上
LogHelper.i("日志内容");
LogHelper.i("md5值为:%s",md5);
- 控制台输出结果:
2020-03-12 18:18:59.881 32462-32462/com.xmexe.exedevv3 I/EXE_TAG: [1] TestClass.testMethod:日志内容
2020-03-12 18:18:59.881 32462-32462/com.xmexe.exedevv3 I/EXE_TAG: [24521] TestClass.testMethod2:md5值为:XXXXXXXX
- 说明
在控制台打印出的日志格式为:[所在线程id] 所在类名.所在方法:日志内容
注意:这里类名也可能是内部类的类名
开关控制
从android.util.Log
源码中可以看出,日志级别排序如下:
VERBOSE<DEBUG<INFO<WARN<ERROR<ASSERT
默认情况下,所有的日志都会在控制台中被打印,但是LogHelper对VERBOSE和DEBUG级别的日志做限制,默认的release包不打印,只有通过如下adb指令,并且重启应用才生效:
//EXE_TAG 是你设置的Tag名
adb -d shell setprop log.tag.EXE_TAG VERBOSE
代码
LogHelper.java
import android.arch.core.BuildConfig;
import android.util.Log;
import java.util.Locale;
import java.util.regex.Pattern;
/**
* Formatted Logging helper class.
*/
public class LogHelper
{
private static String TAG = "EXE_TAG";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE) ||
"debug".equalsIgnoreCase(BuildConfig.BUILD_TYPE);//debug包不需要做限制
public static void v(Object arg)
{
if (DEBUG) // 对VERBOSE级别日志做限制
{
String message = arg == null ? "null" : arg.toString();
Log.v(TAG, buildMessage("%s", message));
}
}
public static void v(String format, Object... args)
{
if (DEBUG)
Log.v(TAG, buildMessage(format, args));
}
public static void d(Object arg)
{
if (DEBUG) // 对DEBUG级别日志做限制
{
String message = arg == null ? "null" : arg.toString();
Log.d(TAG, buildMessage("%s", message));
}
}
public static void d(String format, Object... args)
{
if (DEBUG)
Log.d(TAG, buildMessage(format, args));
}
public static void e(Object arg)
{
String message = arg == null ? "null" : arg.toString();
Log.e(TAG, buildMessage("%s", message));
}
public static void e(String format, Object... args)
{
Log.e(TAG, buildMessage(format, args));
}
public static void i(String format, Object... args)
{
Log.i(TAG, buildMessage(format, args));
}
public static void e(Throwable tr, String format, Object... args)
{
Log.e(TAG, buildMessage(format, args), tr);
}
public static void wtf(String format, Object... args)
{
Log.wtf(TAG, buildMessage(format, args));
}
public static void wtf(Throwable tr, String format, Object... args)
{
Log.wtf(TAG, buildMessage(format, args), tr);
}
/**
* Formats the caller's provided message and prepends useful info like
* calling thread ID and method name.
*/
private static String buildMessage(String format, Object... args)
{
String msg = (args == null) ? format : String.format(Locale.US, format, args);
StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
String caller = "<unknown>";
for (int i = 2; i < trace.length; i++)
{
Class<?> clazz = trace[i].getClass();
if (!clazz.equals(LogHelper.class))
{
String callingClass = trace[i].getClassName();
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
String endWords = callingClass.substring(callingClass.lastIndexOf('$') + 1);
if (!isNumeric(endWords))
{
callingClass = endWords;
}
caller = callingClass + "." + trace[i].getMethodName();
break;
}
}
return String.format(Locale.US, "[%d] %s: %s",
Thread.currentThread().getId(), caller, msg);
}
private static boolean isNumeric(String str)
{
Pattern pattern = Pattern.compile("[0-9]+");
return pattern.matcher(str).matches();
}
}
注意:
INFO及以上级别的日志默认都是会打印的,也就是Log.isLoggable(TAG, Log.INFO)
默认为true。而Log.isLoggable(TAG, Log.VERBOSE)
、Log.isLoggable(TAG, Log.DEBUG)
默认为false,可通过adb -d shell setprop
开启,以此来进行release包的日志控制。