一个简单实用的android日志工具类

4,436 阅读3分钟

简单介绍

android自带的Log类只能打印出tag标签和日志内容,有如下缺点:

  1. 过滤起来不方便,往往单单看控制台的日志信息,难以定位日志所在源代码处
  2. 无法获取执行代码所在的线程。
  3. 无法在release包中统一控制日志开关

LogHelper通过对系统Log类进行代理封装,相比系统Log类有如下优势:

  1. 自动打印出所在方法、类,精确定位源码处
  2. 打印线程信息
  3. 提供字符串格式化参数的功能
  4. 支持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包的日志控制。