本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Square logcat是一款小巧的Kotlin API, 在Android常规Log类基础之上, 进行轻量级的日志打印.
安装
首先, 在build.gradle文件中添加logcat依赖:
dependencies {
implementation 'com.squareup.logcat:logcat:0.1'
}
之后, 在Application#onCreate函数中进行初始化:
// Log all priorities in debug builds, no-op in release builds.
AndroidLogcatLogger.installOnDebuggableApp(this, minPriority = VERBOSE)
Square logcat在release版本中不起任何作用, 仅仅在debug版本中进行日志打印.
API及其使用
logcat()函数有3个参数, 分别是 priority: Int, tag: String, 和生成String的lambda. 其中priority&tag是可选的, 而生成String的lambda则是必选的. 只有在logger已经安装且logger认为当前优先级可以打印日志时, lambda才会被评估.
priority的默认值为 LogPriority.DEBUG.
tag的默认值是日志调用地的类名字, 且没有任何额外的运行时消耗. 之所以没有额外的运行时消耗, 是因为logcat()函数是Any的内联扩展函数, 能够访问Any#this, 而从this当中能够解析出类的名字. 若要从没有this的独立函数中打印日志, 则使用要求tag参数的logcat扩展.
logcat函数并不接收Throwable参数. 相反, 该库提供了Throwable扩展函数: Throwable.asLog(), 该扩展函数返回了一个用于日志输出的String.
import logcat.LogPriority.INFO
import logcat.asLog
import logcat.logcat
class MouseController {
fun play() {
val state = "CHEEZBURGER"
logcat { "I CAN HAZ $state?" }
// logcat output: D/MouseController: I CAN HAZ CHEEZBURGER?
logcat(INFO) { "DID U ASK 4 MOAR INFO?" }
// logcat output: I/MouseController: DID U ASK 4 MOAR INFO?
logcat { exception.asLog() }
// logcat output: D/MouseController: java.lang.RuntimeException: FYLEZ KERUPTED
// at sample.MouseController.play(MouseController.kt:22)
// ...
logcat("Lolcat") { "OH HI" }
// logcat output: D/Lolcat: OH HI
}
}
Motivations
该库的产生是为了满足Square应用Point of Sale的特定需求. 在Square logcat之前, 应用Point of Sale重度使用了Timber. Timber拥有简洁的API且它的DebugTree有能力自动地找出在哪个类中调用及将该类的名字作为tag.
之所以要在应用Point of Sale中替换Timber为Square logcat, 是因为如下原因:
- Kotlin对字符串插入的支持非常好. 将这一点用于日志是一个很令人兴奋的想法. 不幸的是, 如果日志因为什么原因禁止了话, 字符串的插入将会非常昂贵且浪费CPU. 通过使用内联的字符串生成的lambda, 在日志被禁止的时候,
logcat()会在没有性能损耗的前提下支持字符串插入. - Timber的
DebugTree通过创建栈迹的方式捕获调用类的名字作为tag, 是非常昂贵的. 通过将logcat()作为Any的扩展函数, 则能够调用this::class.java且可以在没有创建栈迹的情况下获得调用的上下文. - 当前的实现使用最简单的外部类名字作为
tag, 比如在最后一个.和第一个$之间的字符串. 尽管这并不总是你想要的. 而且, 从抽象类中打印日志时,tag将会是运行时子类的名字. 最终发现这些限制对于我们而言目前为止还算可以. - 大多数时间里, 开发人员仅仅在不考虑到优先级的情况下"发送一些东西给logcat".
logcat()挑选了"debug"作为恰当的默认值以提供大型项目中的持续性. 将priority作为参数也意味着只需学习一个函数, 而且在写日志之前甚至不必学习/思考优先级的事. 在有几个参数要求重载时, 这变得尤其重要(比如在Timber中, (6个优先级 + 1个泛化日志方法) * 3 个重载 = 21 个方法可供选择). - 不要
Throwable参数也是有意而为.Throwable参数仅仅创建了更多的重载和混淆而且 (比如, "参数的顺序是什么?"), 因为真正的日志是关于字符串的, 你所需要的所有仅仅是一种简单的方式将Throwable转化为可打印的字符串. 由此产生了Throwable.asLog(). logcat()这个名字是故意让人厌烦的, 且与Android命令行工具完全相同. 这使得记忆更加容易, 开发者准确地知道这是做什么的, 比如, 写入本地设备. 开发者可以安装自定义logger在release构建中发送日志到远端, 但并不推荐这么做: 根据我们的经验, 远程日志在代码上应该与本地日志不同, 并且可以清楚地识别, 因为其对容量和性能的影响应该非常明显.- 安装logger的API不同于写日志的API, 因为这些操作出现在完全不同的上下文.
以上就是在已经重度使用Timber的情况下, 我们使用选择换到Square logcat的原因.