OTLogger 日志库:轻松定制属于你的日志

1,849 阅读8分钟

简要

OTLogger是一个Android日志库,官方Log 高替版。允许同时把日志 输出到Logcat 和 保存到磁盘。因其内部实现采用策略的设计模式,所以使用者可以根据自己的需求轻松定制该库的每个模块,包括日志输出格式、日志保存方式、捕获异常日志等。

简单使用

在 root/build.gradle 中添加

allprojects {
          repositories {
		...
		maven { url 'https://jitpack.io' }
          }
}

在项目中添加依赖:release 见最新版本号

 implementation 'com.github.oi-october:OTLogger:1.1.0'

使用OTLogger

//使用 QTlogger
LogUtils.v(TAG, "V级别 日志")  
LogUtils.d(TAG, "D级别 日志") 
LogUtils.i(TAG, "I级别 日志")  
LogUtils.w(TAG, "W级别 日志")  
LogUtils.e(TAG, "E级别 日志")  

就是这样简单,如同 Log 一样直接使用即可。默认会把日志打印到控制台,其输出的日志格式与Log的格式一致。

定制属于你的日志

以上是OTLogger最简单的使用方式,但是OTLogger能提供的并不仅仅局限于此,你可以基于OTLogger定制一个想要的日志库。 其中定制的内容包括日志输出格式,日志保存策略,异常日志捕获,日志输出等级等功能。如果你认为上面的功能还不能满足你的需求,你完全可以自己实现OTLogger中对应的模块。

既然要定制,我们先了解OTLogger的实现机制: 框架图

上图可以看到,LogUtils下调用的就是Logger,所以我们定义属于自己的日志无非就是定义一自己的Logger。而自定义一个Logger的常规设置是:

val logger = Logger.Builder()
    .setLogcatPrinter(LogcatDefaultPrinter())   //设置 Logcat printer 
    .setLogTxtPrinter(LogTxtDefaultPrinter())   //设置 LogTxt printer 
    .setCrashStrategy(DefaultCrashStrategyImpl()) //设置 crash ,捕获异常日志
    .build()
LogUtils.setLogger(logger)

Logger下分为三层:

Printer 层 :打印机层

负责把日志打印到指定的位置,无论是把日志输出到Logcat还是磁盘,都是通过打印机(Printer)进行输出。目前已经定义有两种类型的打印机:

1. BaseLogcatPrinter: Logcat打印机

负责打印日志到Logcat, 是一个抽象类。当我们需要定制自己的Logcat Printer 的时候,可以继承BaseLogcatPrinter并重写内部的方法即可:

  • LogcatDefaultPrinter : OTLogger内部已经实现的 Logcat Printer
/**
 * 默认Loacat 打印机
 * @property printable  是否打印日志到Logcat,默认 true
 * @property minLevel   最小日志输出级别, 默认 LogLevel.V
 * @property formatStrategy 日志格式策略, 默认 LogcatDefaultFormatStrategy
 */
open class LogcatDefaultPrinter(
    val printable: Boolean = true,
    val minLevel: LogLevel = LogLevel.V,
    val formatStrategy: LogcatDefaultFormatStrategy = LogcatDefaultFormatStrategy()
) : BaseLogcatPrinter() 

可以看到,输出到Logcat 的日志所有的行为在BaseLogcatPrinter内部实现,包括日志级别,日志格式等。如果你认为LogcatDefaultPrinter已经无法满足你的需求,请重写BaseLogcatPrinter

2.BaseLogTxtPrinter: 磁盘打印机

负责打印日志到磁盘文件中,是一个抽象类。当我们需要定制自己的 LogTxt Printer 的时候,可以继承并重写BaseLogTxtPrinter内部的方法即可。

  • LogTxtDefaultPrinter:OTLogger内部已经实现的 LogTxt Printer
/**
 * 默认日志文件打印机
 * @property printable 是否写入到文件 ,默认 true
 * @property minLevel  最低输出日志, 默认 LogLevel.V
 * @property formatStrategy 日志格式策略, 默认 LogTxtDefaultFormatStrategy
 * @property diskStrategy 文件管理策略,默认 TimeLogDiskStrategyImpl
 */
open class LogTxtDefaultPrinter(
    val printable: Boolean = true,
    val minLevel: LogLevel = LogLevel.V,
    val formatStrategy: LogTxtDefaultFormatStrategy = LogTxtDefaultFormatStrategy(),
    val diskStrategy: BaseLogDiskStrategy = TimeLogDiskStrategyImpl()
) : BaseLogTxtPrinter()

Format 层:日志格式层

该层定义了日志输出的格式,通过继承BaseFormatStrategy实现,每个Printer都包含一个FormatStrategy,所以每个Printer 都可以拥有自己的日志格式。 OTLogger已经实现两种日志格式,DefaultFormatStragety 和 PrettyFormatStrategy 。:

使用DefaultFormatStragety 格式

该日志格式和官方默认Log输出格式(旧版)保持一致,是默认日志格式策略

val logger = Logger.Builder()
             //指定 Logcat 使用日志格式:LogcatDefaultFormatStrategy
             .setLogcatPrinter(LogcatDefaultPrinter(formatStrategy = LogcatDefaultFormatStrategy()))
             //指定 Logtxt 使用日志格式:LogTxtDefaultFormatStrategy
             .setLogTxtPrinter(LogTxtDefaultPrinter(formatStrategy = LogTxtDefaultFormatStrategy()))
             .build()
LogUtils.setLogger(logger)                   

日志格式输出如下:

使用 PrettyFormatStrategy 格式

val logger = Logger.Builder()
             //指定 Logcat 使用日志格式:LogcatPrettyFormatStrategy
             .setLogcatPrinter(LogcatDefaultPrinter(formatStrategy = LogcatPrettyFormatStrategy()))
             //指定 Logtxt 使用日志格式:LogTxtPrettyFormatStrategy
             .setLogTxtPrinter(LogTxtDefaultPrinter(formatStrategy = LogTxtPrettyFormatStrategy()))
             .build()
LogUtils.setLogger(logger)  

输出日志格式如下:

注意: 上面每一种日志格式 Logcat 和 LogTxt 个有一个实现类
比如:LogcatDefaultFormatStrategy 和 LogTxtDefaultFormatStrategy

为什么会这样?

  1. 输出到Logcat的日志本质上还是调用Log.println(int priority, String tag, String msg) 函数进行打印,所以我们无法定制输出的时间、PID 等信息;只能定制其中的 msg 信息,所以专属Logcat的BaseFormatStrategy.format() 返回的String对应的msg参数。
  2. 相对的,LogTxt的BaseFormatStrategy.format()返回的String对应打印到磁盘的整条日志信息。

如果我们希望定义自己的日志打印格式,可以继承BaseFormatStrategy并实现其内部format方法

/**
 * @param logLevel  日志等级
 * @param tag  日志tag
 * @param msg  日志内容
 * @param thr  异常日志内存
 * @param param 预留拓展参数,方便更好定制日志格式。
 * @return 日志输出格式
 */
abstract fun format(logLevel: LogLevel, 
                    tag:String?, msg:String?,
                    thr: Throwable?,
                    param:Any? = null):String

Disk层:磁盘管理层

该层管理了所有的日志文件,只有 LogTxt Printer才会使用到,主要解决的问题:

每一行日志打印打印到哪个文件中?
何时分配一个新的日志文件?
何时回收、如何回收旧的日志文件?

该层通过通过继承抽象类BaseLogDiskStrategy实现。目前已经实现的DiskStrategy

TimeLogDiskStrategyImpl:时间管理策略

按时间管理日志(默认磁盘管理策略

-   默认每个日志文件保存七天,
-   默认按照小时创建日志文件
-   默认文件名 log_年_月_日_时间段.log 。 eg:otLog_2023_02_12_15_16.log ,这里的 15_16 表示该文件储存 15点到 16点的日志。

其构造方法如下:
```kotlin
/**
 * @param logDirectory 日志文件夹
 * @param logKeepOfDay 日志保存天数
 * @param logSegment 创建日志文件间隔,默认每个小时创建一份新的日志文件
 */
open class TimeLogDiskStrategyImpl(
    val logDirectory: String = defaultLogDir,
    val logKeepOfDay: Int = 7,
    val logSegment:LogTimeSegment= LogTimeSegment.ONE_HOUR
) : BaseLogDiskStrategy() 
```

FileLogDiskStrategyImpl:文件管理策略

按照文件大小管理磁盘日志

- 默认每个日志文件5MB,参考[`logFileStoreSizeOfMB`]
- 默认日志文件夹最大可容纳 100M日志,超过[`logDirectoryMaxStoreSizeOfMB`]会按照时间顺序删除旧的日志,直到低于预定值
- 默认文件名 otLog_年__日__分_秒.log 。 eg: otLog_2023_02_12_16_28_56.log
- 每个日志写满了会创建一个新的日志文件
- 为了保护系统,以上都要当系统可用空闲空间大于最低限制的空闲空间[`minFreeStoreOfMB`]时,才会创建新的日志文件。

其构造方法如下:

/**
 * @param logDirectory 日志文件夹
 * @param minFreeStoreOfMB 最小空闲空间(单位MB),当系统最小空闲存储空间低于该值时,不再创建新的日志文件
 * @param logDirectoryMaxStoreSizeOfMB 日志文件夹最大的存储容量(单位MB),所有的日志文件加起来的大小不得操过该值
 * @param logFileStoreSizeOfMB 每个日志文件容量(单位MB),只有上一个日志文件操过容量,才会创建下一个日志文件
 */
open class FileLogDiskStrategyImpl(
    val logDirectory: String = defaultLogDir,
    val minFreeStoreOfMB: Int = 200,
    val logDirectoryMaxStoreSizeOfMB: Int = 100,
    val logFileStoreSizeOfMB:Int = 5
) : BaseLogDiskStrategy()

TimeLogDiskStrategyImpl:文件+时间管理策略

同时具备[FileLogDiskStrategyImpl] 和 [TimeLogDiskStrategyImpl] 的部分特性

- 默认日志文件夹最大可容纳 100M日志,超过[`logDirectoryMaxStoreSizeOfMB`]会按照时间顺序删除旧的日志,直到低于预定值; 
- 默认文件名 默认文件名 otlog_年_月_日_时间段_时间戳.log 。 eg: otLog_2023_02_12_16_17_11123223423423.log ; 
-  每个日志写满了会创建一个新的日志文件,超过日志时间片段,会创建一个新的日志文件进行存储; - 为了保护系统,以上都要当系统可用空闲空间大于最低限制的空闲空间[`minFreeStoreOfMB`]时,才会创建新的日志文件。

其构造函数如下:

/**
 * @param logDirectory 日志文件夹
 * @param minFreeStoreOfMB 最小空闲空间(单位MB),当系统最小空闲存储空间低于该值时,不再创建新的日志文件
 * @param logDirectoryMaxStoreSizeOfMB 日志文件夹最大的存储容量(单位MB),所有的日志文件加起来的大小不得操过该值
 * @param logFileStoreSizeOfMB 每个日志文件容量(单位MB),只有上一个日志文件操过容量,才会创建下一个日志文件
 * @param segment 创建日志文件间隔,默认每个小时创建一份新的日志文件
 *
 */
open class FileAndTimeDiskStrategyImpl(
    val logDirectory: String = defaultLogDir,
    val minFreeStoreOfMB: Int = 200,
    val logDirectoryMaxStoreSizeOfMB: Int = 100,
    val logFileStoreSizeOfMB: Int = 5,
    val segment: LogTimeSegment = LogTimeSegment.ONE_HOUR
) : BaseLogDiskStrategy()

设置磁盘管理策略

 val logger = Logger.Builder()
              .setLogTxtPrinter(LogTxtDefaultPrinter(diskStrategy = TimeLogDiskStrategyImpl()) //time
              // LogTxtDefaultPrinter(diskStrategy = FileLogDiskStrategyImpl())  //file
              // LogTxtDefaultPrinter(diskStrategy = FileAndTimeDiskStrategyImpl()) // file & time
             )
             .build()
LogUtils.setLogger(logger)

设置日志压缩策略

OTLogger 打印到disk的日志文件默认是不压缩的。在v1.2.0版本以后,添加了日志压缩策略,其压缩规则是:

- 每当创建一个新的日志文件,遍历所有旧的日志文件进行压缩;
- 当前正在写入的日志文件不压缩;
  • 当前已经实现的压缩策略:
/**
 * 压缩日志文件成为zip包
 */
class ZipLogCompressStrategy : BaseLogCompressStrategy() 

所有的压缩策略必须继承于 BaseLogCompressStrategy。如果有需要,你可以实现自己的压缩策略。

  • 使用压缩策略:
    压缩策略必须结合磁盘管理策略一起使用。比如:
//案例1:
val diskStrategy = FileAndTimeDiskStrategyImpl().also {
                     it.setLogCompressStrategy(logCompressStrategy) //设置压缩策略
                   }
//案例2:
val diskStrategy= TimeLogDiskStrategyImpl().also {
                    it.setLogCompressStrategy(logCompressStrategy) //设置压缩策略
            	  }

异常日志捕获

val logger = Logger.Builder()
             .setCrashStrategy(DefaultCrashStrategyImpl()) //设置 crash ,捕获异常日志
             .build()
LogUtils.setLogger(logger)

通过上面配置,可以捕获app异常,并且把异常信息打印到 Logcat 或者 LogTxt

github 传送门

OTLogger