17、扩展函数与高阶函数在日志与调试中的应用
日志和调试,说白了就是开发者的眼睛。我做了这么多年Android,最怕的就是线上出问题,日志却一片空白。今天咱们聊聊怎么用Kotlin的扩展函数和高阶函数,把这双眼睛擦得更亮。
一、Log的扩展函数:告别重复代码
你想想看,每次写日志都要敲Log.d(TAG, "xxx"),是不是很烦?我习惯给Log类写个扩展,让调用更简洁。
// 给Log类添加扩展函数
fun Log.d(tag: String, message: () -> String) {
if (Log.isLoggable(tag, Log.DEBUG)) {
Log.d(tag, message())
}
}
// 使用示例
Log.d("MainActivity") { "用户点击了按钮,当前状态:$state" }
这里有个小技巧:用message: () -> String代替直接传字符串。好处是——只有当日志级别开启时,才会执行lambda里的字符串拼接。我在项目中遇到过日志拼接导致性能问题的坑,用这个方式就完美避开了。
核心思路:扩展函数让调用更简洁,高阶函数让计算更懒。
二、调试信息的DSL输出:结构化日志
普通日志是一行行的字符串,看多了眼花。我更喜欢用DSL把调试信息组织成结构化的格式。举个例子:
// 调试信息DSL
class DebugInfo {
private val sections = mutableListOf<String>()
fun section(title: String, block: SectionBuilder.() -> Unit) {
val builder = SectionBuilder()
builder.block()
sections.add("[$title] ${builder.build()}")
}
override fun toString() = sections.joinToString("\n")
}
class SectionBuilder {
private val items = mutableListOf<String>()
infix fun String.`=`(value: Any?) {
items.add("$this = $value")
}
fun build() = items.joinToString(", ")
}
// 使用
fun logDebugInfo(block: DebugInfo.() -> Unit) {
val info = DebugInfo()
info.block()
Log.d("Debug", info.toString())
}
// 调用
logDebugInfo {
section("网络请求") {
"url" `=` "https://api.example.com"
"method" `=` "GET"
"耗时" `=` "230ms"
}
section("用户信息") {
"id" `=` 1001
"name" `=` "张三"
}
}
输出效果:
[网络请求] url = https://api.example.com, method = GET, 耗时 = 230ms
[用户信息] id = 1001, name = 张三
嗯,这里要注意:infix关键字让=看起来像赋值操作,但实际上是个函数调用。我第一次用这个写法时也觉得怪,但用顺手后真香。
我的习惯:调试DSL只用在开发阶段,发布版本要关掉。可以加个BuildConfig.DEBUG判断。
三、性能监控的简化:一行代码搞定
性能监控最怕什么?侵入业务代码。我曾经为了监控一个函数的执行时间,在函数前后各加一行System.currentTimeMillis(),结果代码变得又臭又长。
用高阶函数封装一下:
// 性能监控工具
inline fun <T> measureTime(
tag: String,
block: () -> T
): T {
val start = System.nanoTime()
val result = block()
val duration = (System.nanoTime() - start) / 1_000_000
Log.d(tag, "执行耗时:${duration}ms")
return result
}
// 使用
val data = measureTime("数据库查询") {
database.query("SELECT * FROM users")
}
你看,业务逻辑完全没被污染。而且inline关键字让lambda在编译时展开,没有额外开销。
避坑指南:我曾经在循环里用
measureTime,结果日志刷屏。建议加上采样逻辑,比如每100次才输出一次。
四、知识体系总览
下面这张图总结了本章的核心逻辑:
五、实战组合:一个完整的调试工具
把上面三个技巧组合起来,就是一个轻量级的调试框架:
// 组合使用
class DebugTool {
companion object {
inline fun <T> trace(
tag: String,
section: String = "",
block: DebugInfo.() -> T
): T {
val info = DebugInfo()
val start = System.nanoTime()
val result = info.block()
val duration = (System.nanoTime() - start) / 1_000_000
if (section.isNotEmpty()) {
info.section("性能") {
"耗时" `=` "${duration}ms"
}
}
Log.d(tag, info.toString())
return result
}
}
}
// 调用
val user = DebugTool.trace("UserModule", section = "加载用户") {
section("数据源") {
"来源" `=` "本地缓存"
"key" `=` "user_1001"
}
loadUserFromCache(1001)
}
这个工具我一直在用。它把日志、调试信息、性能监控三合一,而且对业务代码零侵入。你想想看,如果每个函数都这么写,排查问题是不是快多了?
总结一下:扩展函数让调用更简洁,高阶函数让逻辑更灵活。两者结合,日志调试不再是苦差事。
公众号:蓝海资料掘金营,微信deep3321