Logger框架源码解析

来源:互联网 发布:叮叮聊天软件 编辑:程序博客网 时间:2024/06/08 02:28

在移动软件开发中,我们经常会用到很多框架,如网络框架retrofit,图片加载框架glide,数据库框架litepal,日志框架Logger等。这些框架对应用都很重要,日志框架也是其中重要的一部分。因为很多操作如调试,优化,修改bug等都需要通过日志来验证我们的想法,所以日志框架就成项目的必需。本篇博文将会介绍Logger日志框架,主要从源码的角度分析。

一、什么是Logger框架?

Simple, pretty and powerful logger for android。简单,功能强大日志框架,专为Android。

Logger有多强大呢?先让我们来看一张打印日志图:

这里写图片描述

从此图,就可看出Logger打印日志相当强大。Logger框架原理图:

这里写图片描述

二、Logger源码解析(Json)

Logger源码地址:https://github.com/orhanobut/logger

本篇博文主要采用版本V2.1.1的源码。

1.Logger初始化

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

我们先来看Logger的初始化,进入Logger类

/** * But more pretty, simple and powerful */public final class Logger {  private static Printer printer = new LoggerPrinter();  ......  public static void addLogAdapter(LogAdapter adapter) {    printer.addAdapter(adapter);  }......}

Logger的静态方法,主要就是向printer中添加了一个Adapter,通过定义我们知道printer为LoggerPrinter,我们继续看看LoggerPrinter

class LoggerPrinter implements Printer {  private final List<LogAdapter> logAdapters = new ArrayList<>();  ......  @Override public void addAdapter(LogAdapter adapter) {    logAdapters.add(adapter);  }......}

从方法名addLogAdapter就知向LogAdapter列表添加LogAdapter,源码下来也的确如此,但这LogAdapters有啥用呢?后面会说到。让我们再来看看AndroidLogAdapter

public class AndroidLogAdapter implements LogAdapter {  private final FormatStrategy formatStrategy;  public AndroidLogAdapter() {    this.formatStrategy = PrettyFormatStrategy.newBuilder().build();  }  public AndroidLogAdapter(FormatStrategy formatStrategy) {    this.formatStrategy = formatStrategy;  }......}

在AndroidLogAdapter中初始化了FormatStrategy,这类有啥用呢?让我们来看看

public interface FormatStrategy {  void log(int priority, String tag, String message);}

FormatStrategy就是一个接口,应该是打印日志的。我们来看看他的实现类PrettyFormatStrategy.newBuilder().build(),继续看PrettyFormatStrategy源码

public class PrettyFormatStrategy implements FormatStrategy {  private final int methodCount;  private final int methodOffset;  private final boolean showThreadInfo;  private final LogStrategy logStrategy;  private final String tag;  private PrettyFormatStrategy(Builder builder) {    methodCount = builder.methodCount;    methodOffset = builder.methodOffset;    showThreadInfo = builder.showThreadInfo;    logStrategy = builder.logStrategy;    tag = builder.tag;  }  public static Builder newBuilder() {    return new Builder();  }  public static class Builder {    .......    private Builder() {    }    ......    public PrettyFormatStrategy build() {      if (logStrategy == null) {        logStrategy = new LogcatLogStrategy();      }      return new PrettyFormatStrategy(this);    }  }

由上易知,主要就是对PrettyFormatStrategy初始化,然后赋值给AndroidLogAdapter的FormatStrategy。PrettyFormatStrategy类对Logger来说是核心类,因为所有日志的打印控制主要也在此类实现,接下来我们会说到。

2.Logger打印日志

    Logger.d("Hello world!");//2.debug日志    Logger.json("{ \"key\": 3, \"value\": something}");//3.Json日志

i.debug日志,一般日志打印
首先,我们来看一般日志,debug日志,进入Logger源码

public final class Logger {  private static Printer printer = new LoggerPrinter();  .....  public static void d(String message, Object... args) {    printer.d(message, args);  }  public static void d(Object object) {    printer.d(object);  }  public static void e(String message, Object... args) {    printer.e(null, message, args);  }  public static void e(Throwable throwable, String message, Object... args) {    printer.e(throwable, message, args);  }  ......}

主要是执行printer中的方法,我们继续看LoggerPrinter

class LoggerPrinter implements Printer {   ......  @Override public void d(String message, Object... args) {    log(DEBUG, null, message, args);  }  @Override public void d(Object object) {    log(DEBUG, null, Utils.toString(object));  }  @Override public synchronized void log(int priority, String tag, String message, Throwable throwable) {    .......    for (LogAdapter adapter : logAdapters) {      if (adapter.isLoggable(priority, tag)){        adapter.log(priority, tag, message);      }    }  }}

可以发现,主要就是遍历LogAdapters列表,因为adapter是不同的适配器,不同适配器有不同的打印日志信息格式。从Logger的初始化,我们知道传入的是AndroidLogAdapter并且AndroidLogAdapter中主要执行的是PrettyFormatStrategy中的log方法,这里我们直接看PrettyFormatStrategy中的log方法

  @Override public void log(int priority, String onceOnlyTag, String message) {    String tag = formatTag(onceOnlyTag);    logTopBorder(priority, tag);//1.Log顶端的线格式    logHeaderContent(priority, tag, methodCount);//2.log头部内容    //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);//3.log日志内容      logBottomBorder(priority, tag);//4.log底端线格式      return;    }    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));//5.log日志内容    }    logBottomBorder(priority, tag);//6.log底端线格式  }

此方法是Logger框架打印日志的核心方法,上面图片中的日志,主要就是通过这个方法控制打印的。让我们来看相关方法

public class PrettyFormatStrategy implements FormatStrategy {  private static final char TOP_LEFT_CORNER = '┌';  private static final char BOTTOM_LEFT_CORNER = '└';  private static final char MIDDLE_CORNER = '├';  private static final char HORIZONTAL_LINE = '│';  private static final String DOUBLE_DIVIDER = "────────────────────────────────────────────────────────";  private static final String SINGLE_DIVIDER = "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄";  private static final String TOP_BORDER = TOP_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER;  private static final String BOTTOM_BORDER = BOTTOM_LEFT_CORNER + DOUBLE_DIVIDER + DOUBLE_DIVIDER;  private static final String MIDDLE_BORDER = MIDDLE_CORNER + SINGLE_DIVIDER + SINGLE_DIVIDER;  .....  private void logTopBorder(int logType, String tag) {    logChunk(logType, tag, TOP_BORDER);  }  private void logBottomBorder(int logType, String tag) {    logChunk(logType, tag, BOTTOM_BORDER);  }  private void logDivider(int logType, String tag) {    logChunk(logType, tag, MIDDLE_BORDER);  }  private void logContent(int logType, String tag, String chunk) {    String[] lines = chunk.split(System.getProperty("line.separator"));//核心方法    for (String line : lines) {      logChunk(logType, tag, HORIZONTAL_LINE + " " + line);    }  }  private void logChunk(int priority, String tag, String chunk) {    logStrategy.log(priority, tag, chunk);  }......}

由上易知,最后都调用logChunk()方法,最后主要也是调用了logStrategy的log方法,通过Logger初始化,知logStrategy就是LogcatLogStrategy类,我们来看LogcatLogStrategy中的log方法

import android.util.Log;public class LogcatLogStrategy implements LogStrategy {  @Override public void log(int priority, String tag, String message) {    Log.println(priority, tag, message);  }}

这里主要调用了android原生的打印日志方法,从而日志就被打印在logCat中了。到这里,一般日志打印就介绍完了,其他日志可以类推的,这里不介绍了。

ii.Json日志的打印

打印调用方法

Logger.json("{ \"key\": 3, \"value\": something}");//3.Json日志

从debug日志打印分析中,我们知道PrettyFormatStrategy的log类为打印日志核心方法,对于json日志打印,主要是打印内容的区别,其他打印没有区别,所以这里主要看看打印日志的方法

  private void logContent(int logType, String tag, String chunk) {    String[] lines = chunk.split(System.getProperty("line.separator"));//核心方法    for (String line : lines) {      logChunk(logType, tag, HORIZONTAL_LINE + " " + line);    }  }

这里知道,对打印内容chunk进行了分割,主要以System.getProperty(“line.separator”)即换行符\n进行分割。从打印Json内容中,我们没有发现换行符,这里需要看看最初调用的方法。由Logger初始化,我们知Logger.json()方法主要就是调用LoggerPrinter中的json方法,这里让我们来看LoggerPrinter.json()方法

 @Override public void json(String json) {    if (Utils.isEmpty(json)) {      d("Empty/Null json content");      return;    }    try {      json = json.trim();      if (json.startsWith("{")) {        JSONObject jsonObject = new JSONObject(json);        String message = jsonObject.toString(JSON_INDENT);//1        d(message);        return;      }      if (json.startsWith("[")) {        JSONArray jsonArray = new JSONArray(json);        String message = jsonArray.toString(JSON_INDENT);//2.        d(message);        return;      }      e("Invalid Json");    } catch (JSONException e) {      e("Invalid Json");    }  }

通过调试,知道就是在注释中的位置,在进行转化的时候,字符串”{ \”key\”: 3, \”value\”: something}”转为”{\n”key”: 3,\n”value”: something\n}”,从而在打印的时候,就可以分行打印键值对。这样也就再打印json的时候可以分行显示。

到这里,日志Logger框架原理分析就讲解完。其中logger框架还支持打印xml,具体原理如何,这里不做介绍了。

三、总结

Logger日志框架,让Log日志变得整洁,简单,易看,一大功德。其中PrettyFormatStrategy通过静态内部类实现了Logger显示内容配置方式值得借鉴。

四、相关参考文档

Looger官方介绍