自制Android Log框架全过程

2,155 阅读5分钟

1. 前言,编写此框架的目的

由于公司内部android团队没有一个统一的日志框架。各个开发成员使用log的方式参差不一。作为一个有追求的程序员,觉得写一个日志框架在团队内部推广使用。当然网上也有现场的Log框架使用,但是由于公司业务和设备的特殊性,还是决定自己写一个。

刚好前阵子在学习设计模式相关的知识,可以用这个框架来练练手。(强烈推荐极客时间上面“设计模式之美”,循序渐进、通俗易懂,gk.link/a/10iK4)

另外再吐槽两点,供大家借鉴改进:

  1. 正确使用log日志级别。工作期间,阅读同事写的代码,经常发现同事经常乱用log日志级别,例如明明不应该用error级别的log,却用了error级别来打印(例如打印一个通过接口获取到的list数据)。 也许使用者在调试的时候可以用error的“红色”信息来观察,确实比较显眼一点,但是平时其他人观察logcat信息的时候,被一堆红色字体充斥着眼球,这感觉真的。。。
  2. 正确使用log的tag。个人认为,tag是用来归类同一组功能的日志的,但是发现有些同学喜欢用他们的名字来作为tag。

因此最近关于log的正确使用,被我们列入了公司的编程规范里。

2. 整理需求,从模糊慢慢变具体

框架性质,或者说与业务没有太大关联的功能,需求都是比较模糊的,写这类代码,个人觉得最好还是要先整理好需求,把模糊的思想转化为具体的文字需求。

整理成文字需求的好处,每点都很重要:

  1. 方便自己后续设计类时有明确的需求来作为设计参考
  2. 方便自己N久后回顾框架功能
  3. 方便团队或新来成员快速了解框架的来历和需求。(别小看这一点,当你作为老员工,而公司的团队成员流动性很大,需要你一遍又一遍给每一个员工讲解程序逻辑的时候就知道文档的重要性了)

我们先来明确日志的作用,可以归纳为两点:

  1. 方便开发时调试程序。
  2. 方便产品上线后出现问题时快速定位问题。

日志框架的功能,总体来说也分为两点:写 和 读。 咱们根据这两点,仔细思考进行细分,当然,在思考需求的时候,可以根据参考以往的项目经验,或者参考android本身的日志功能。下面是我整理的大致需求:

3. 设计让人纠结的类,类与类之间的职责与关系

通过上面的需求整理,我们能明确此框架大致分为两个部分:读和写。那么我们先设计这两个部分的接口。

1. 写

首先定义一个写的接口LogWriter,我们可以参考android的logcat的展示,写的方法参数应该包含有: time、logVersion、tag、content、threadName。

public interface LogWriter {
    void write(@LogVersion int version, String tag, String content) throws WriteLogErrException;

    void write(@LogVersion int version, long time, String threadName, String tag, String content) throws WriteLogErrException;

    void write(Log log) throws WriteLogErrException;
}

至于实现类,我们按log的存储位置来划分:

  1. LogcatLogWriter
  2. CacheLogWriter
  3. DiskLogWriter
  4. CrashLogWriter(奔溃日志存储在单独的文件里)

性能要求: 我们在前面整理需求的时候提到过,存储log的时候,不能影响软件性能,性能无非是空间和时间,我们这里先考虑时间,如果只是把日志写到logcat和内存,不会占用太大时间,而如果是写到磁盘文件的话,就有可能了,我们这里可以初步设定使用一个“日志存储队列”来应对存储日志到文件的情况。 至于空间要求,我们在下面跟其他方面一起考虑。

2. 读 (Log提取器)

首先定义一个Log提取器接口LogExtractor,由于读取log需要异步处理,所以我们还需要一个读取回调ExtractCallBack

public interface LogExtractor {
    interface ExtractCallBack {
        /**
         * 提取日志成功
         * @param logFile 提取到的日志文件
         */
        void onSuccess(File logFile);

        void onFail(String errMsg);
    }

    /**
     * 提取日志
     *
     * @param extractCallBack 提取日志回调
     */
    void extract(ExtractCallBack extractCallBack);
}

接下来是各个Log提取器的实现类 LogcatExtractor、 DateLogExtractor、TimeLogExtractor、CrashLogExtractor

3. 可配置性,LogConfig

我们需要一个log配置类,提供给用户,用户可以根据项目实际需要来配置log框架。 那么有什么是可以配置的呢?

  1. 是否使用内存缓存
  2. 是否使用磁盘存储
  3. 是否存储奔溃日志
  4. log内存缓存的最大容量
  5. 存储log的文件夹的最大容量
  6. 存储奔溃日志的文件夹的最大容量
  7. 存储log的文件夹的路径
  8. 存储奔溃日志的文件夹的路径

4. LogWriteLogic --日志写入逻辑类

上面“写”的部分我们设计好了LogWriter和各个实现类,但是怎么调用它们呢? 我们可以实现一个逻辑类,可以根据用户传入的配置LogConfig来执行对应的逻辑。 至于它的具体实现细节,我们先不考虑,我们这一部分主要是先大概设计各个类的职责和关系就可以了。

5. 用户统一调用入口

最后,为减低框架的使用难度,一般框架都会设计一个入口类(外观类)来供用户使用。 大致要提供的方法有:

  1. 配置相关 1.1 传入LogConfig
  2. 写日志相关 2.1 写普通日志 2.2 写奔溃日志
  3. 提取日志相关 3.1 从logcat提取日志 3.2 按日期提取日志 3.3 按开始日期和结束日期提取日志 3.4 提取奔溃日志

4. 源码地址

有了前期的设计,编写代码的部分其实不算太难了,直接放源码,有什么问题可以多多指教: github.com/chenyugui/T…