盤點常用的Android開發庫(3) -- Logger

一、簡介

Logger,顧名思義,肯定是和日誌有關。作者本人用三個詞概括了這個Android開源日誌庫的優點:Simple(簡單)、Pretty (漂亮)、Powerful(強大)。

二、使用

2.1 依賴注入

implementation 'com.orhanobut:logger:2.2.0'

2.2 初始化 

在Application或者activity中添加如下代碼:

//簡單初始化
Logger.addLogAdapter(new AndroidLogAdapter());



//修改打印基本配置
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
        .showThreadInfo(false)      //(可選)是否顯示線程信息。 默認值爲true
        .methodCount(2)               // (可選)要顯示的方法行數。 默認2
        .methodOffset(7)               // (可選)設置調用堆棧的函數偏移值,0的話則從打印該Log的函數開始輸出堆棧信息,默認是0
      //.logStrategy("")  //(可選)更改要打印的日誌策略。 默認LogCat
        .tag("——TAG——")                  //(可選)每個日誌的全局標記。 默認PRETTY_LOGGER(如上圖)
        .build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));



//保存log日誌到文件中去,只需要換一個適配器即可,當然讀寫權限還是要申請的
Logger.addLogAdapter(new DiskLogAdapter());



//控制打印開關
//當isLoggable return BuildConfig.DEBUG 就不會打印日誌了,很好的控制了日誌的打印
Logger.addLogAdapter(new AndroidLogAdapter(){
    @Override
    public boolean isLoggable(int priority, @Nullable String tag) {
        return BuildConfig.DEBUG;
    }
});

接下來只需要在需要打印的地方調用Logger的對應打印方法即可。

        Logger.i("information");
        Logger.d("debug");
        Logger.v("verbose");
        Logger.e("error");
        Logger.w("warning");
        Logger.wtf("What a Terrible Failure");
        Logger.json("json");
        Logger.xml("xml");

運行一下項目,日誌內容就會打印出來。然後~就沒有然後了

日誌打印截圖 Logger除了能打印基本的數據類型之外還對其他數據類型打印進行了格式優化,包括Format類型、List,Map,Set等集合類型、Json數據、XML數據。

三、分析

1.初始化

又到了‘扒別人衣服’的時候了,咱們還是從初始化看起,首先調用了Logger的靜態方法addLogAdapter,這個方法又是直接調用LoggerPrinter的addAdapter方法,只是對參數LogAdapter做了個空指針檢測並拋出異常。

public final class Logger {

  ...

  @NonNull private static Printer printer = new LoggerPrinter();

  private Logger() {
    //no instance
  }

  public static void printer(@NonNull Printer printer) {
    Logger.printer = checkNotNull(printer);
  }

  public static void addLogAdapter(@NonNull LogAdapter adapter) {
    printer.addAdapter(checkNotNull(adapter));
  }

  ...
}

LoggerPrinter中的addAdapter則是將LogAdapter適配器存儲到集合中,同理clearLogAdapters就是清空這個適配器的集合了。

  @Override public void clearLogAdapters() {
    logAdapters.clear();
  }

  @Override public void addAdapter(@NonNull LogAdapter adapter) {
    logAdapters.add(checkNotNull(adapter));
  }

至此,初始化就結束了,那麼這個LogAdapter又起到了什麼作用呢,前面我們介紹了初始化的時候傳入實現了LogAdapter接口的對象,比如AndroidLogAdapter。而該對象又持有一個FormatStrategy。所有的打印樣式就是在FormatStrategy的具體對象中實現的。

public class AndroidLogAdapter implements LogAdapter {

  @NonNull private final FormatStrategy formatStrategy;

  public AndroidLogAdapter() {
    this.formatStrategy = PrettyFormatStrategy.newBuilder().build();
  }

  public AndroidLogAdapter(@NonNull FormatStrategy formatStrategy) {
    this.formatStrategy = checkNotNull(formatStrategy);
  }

  @Override public boolean isLoggable(int priority, @Nullable String tag) {
    return true;
  }

  @Override public void log(int priority, @Nullable String tag, @NonNull String message) {
    formatStrategy.log(priority, tag, message);
  }

}

 2.打印

打印各級別各類型日誌都是調用LoggerPrinter具體實現的方法。而這些方法最終都走了LoggerPrinter的log方法,我們具體看下log的過程。

  private synchronized void log(int priority,
                                @Nullable Throwable throwable,
                                @NonNull String msg,
                                @Nullable Object... args) {
    checkNotNull(msg);

    String tag = getTag();
    String message = createMessage(msg, args);
    log(priority, tag, message, throwable);
  }

  @NonNull private String createMessage(@NonNull String message, @Nullable Object... args) 
  {
    return args == null || args.length == 0 ? message : String.format(message, args);
  }

首先使用synchronized同步鎖對打印方法進行鎖定,避免打印時串行顯示,打印格式錯亂。然後檢測msg是否爲空,再調用createMessage方法根據args拍段msg是否是Format類型,如果是則format處理,最後調用重載的log方法。

@Override public synchronized void log(int priority,
                                         @Nullable String tag,
                                         @Nullable String message,
                                         @Nullable Throwable throwable) {
    if (throwable != null && message != null) {
      message += " : " + Utils.getStackTraceString(throwable);
    }
    if (throwable != null && message == null) {
      message = Utils.getStackTraceString(throwable);
    }
    if (Utils.isEmpty(message)) {
      message = "Empty/NULL log message";
    }

    for (LogAdapter adapter : logAdapters) {
      if (adapter.isLoggable(priority, tag)) {
        adapter.log(priority, tag, message);
      }
    }
  }

log方法裏就是處理異常,最後獲取集合內所有的LogAdapter,並根據isLoggable方法(這個前面控制打印開關講過)判斷是否打印,然後分別調用LogAdapter的log方法的具體實現。我們這裏還是看AndroidLogAdapter的log實現。

  @Override public void log(int priority, @Nullable String tag, @NonNull String message) {
    formatStrategy.log(priority, tag, message);
  }

 前面分析初始化時我們說到了AndroidLogAdapter持有的FormatStrategy對象,這裏打印用到了該對象的log方法,繼續往下看。

  @Override public void log(int priority, @Nullable String onceOnlyTag, @NonNull String message) {
    checkNotNull(message);

    String tag = formatTag(onceOnlyTag);
    
    //打印頂部邊框
    logTopBorder(priority, tag);
    //打印頭部內容
    logHeaderContent(priority, tag, methodCount);

    //這裏獲取消息長度,並判斷時候超過ANDROID限制的日誌輸出長度4076個字符
    //get bytes of message with system's default charset (which is UTF-8 for Android)
    byte[] bytes = message.getBytes();
    int length = bytes.length;
    if (length <= CHUNK_SIZE) {
      if (methodCount > 0) {
        logDivider(priority, tag);
      }
      //打印消息內容
      logContent(priority, tag, message);
      //打印底邊
      logBottomBorder(priority, tag);
      return;
    }
    //如果消息長度大於4076個字符,則分多次打印
    if (methodCount > 0) {
      logDivider(priority, tag);
    }
    for (int i = 0; i < length; i += CHUNK_SIZE) {
      int count = Math.min(length - i, CHUNK_SIZE);
      //create a new String with system's default charset (which is UTF-8 for Android)
      logContent(priority, tag, new String(bytes, i, count));
    }
    //打印底邊
    logBottomBorder(priority, tag);
  }

 log打印的具體步驟我寫了註釋,至此整個打印過程結束。

四、參考鏈接

github上Logger庫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章