Android自定义Log函数如何打印协程ID或名称?
解决日志中打印协程ID/名称的问题
方案一:利用kotlinx.coroutines.debug探针获取协程信息
该方案需要依赖kotlinx-coroutines-debug库(建议仅在Debug编译环境引入,避免增加生产包体积),通过探针直接获取当前线程关联的协程详情。
修改后的日志函数代码
import kotlinx.coroutines.debug.DebugProbes @JvmInline value class MyLogger(private val tag: String) { fun log(level: Level, e: Throwable? = null, message: String) { if (isLoggingEnabled(level)) { val threadName = Thread.currentThread().name // 尝试获取当前线程绑定的协程信息 val coroutineInfo = runCatching { DebugProbes.getCoroutineInfo(Thread.currentThread()) }.getOrNull()?.firstOrNull() // 拼接协程ID和名称 val coroutineSuffix = coroutineInfo?.let { info -> val coroutineId = info.context[Job]?.id ?: "-" val coroutineName = info.context[CoroutineName]?.name ?: "coroutine" " @$coroutineName#$coroutineId" } ?: "" val fullThreadInfo = "[$threadName$coroutineSuffix]" val messageWithCoroutineInfo = "Running on: $fullThreadInfo | $message" when (level) { Level.VERBOSE -> Log.v(tag, messageWithCoroutineInfo, e) Level.DEBUG -> Log.d(tag, messageWithCoroutineInfo, e) Level.INFO -> Log.i(tag, messageWithCoroutineInfo, e) Level.WARN -> Log.w(tag, messageWithCoroutineInfo, e) Level.ERROR -> Log.e(tag, messageWithCoroutineInfo, e) Level.ASSERT -> Log.wtf(tag, messageWithCoroutineInfo, e) } } } // ... 保留原有其他方法 }
额外初始化步骤
在Application或Activity的onCreate中(仅Debug环境)初始化探针:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val isDebug = !BuildTypes.isProduction System.setProperty("kotlinx.coroutines.debug", if(isDebug) "on" else "off") // 仅Debug环境安装探针 if (isDebug) { DebugProbes.install() } }
方案二:直接从协程上下文获取信息
该方案无需额外依赖,但要求日志调用必须在协程上下文内执行,通过currentCoroutineContext()直接读取协程的Job ID和名称。
修改后的代码实现
import kotlin.coroutines.coroutineContext @JvmInline value class MyLogger(private val tag: String) { // 新增带协程上下文参数的日志方法 private fun logWithContext(level: Level, e: Throwable? = null, message: String, context: CoroutineContext) { if (isLoggingEnabled(level)) { val threadName = Thread.currentThread().name val job = context[Job] val coroutineName = context[CoroutineName]?.name ?: "coroutine" val coroutineId = job?.id ?: "-" val coroutineSuffix = job?.let { " @$coroutineName#$coroutineId" } ?: "" val fullThreadInfo = "[$threadName$coroutineSuffix]" val messageWithCoroutineInfo = "Running on: $fullThreadInfo | $message" when (level) { Level.VERBOSE -> Log.v(tag, messageWithCoroutineInfo, e) Level.DEBUG -> Log.d(tag, messageWithCoroutineInfo, e) Level.INFO -> Log.i(tag, messageWithCoroutineInfo, e) Level.WARN -> Log.w(tag, messageWithCoroutineInfo, e) Level.ERROR -> Log.e(tag, messageWithCoroutineInfo, e) Level.ASSERT -> Log.wtf(tag, messageWithCoroutineInfo, e) } } } // 修改原有扩展函数,自动传入当前协程上下文 fun i(messageProvider: () -> String) { logWithContext(Level.INFO, message = messageProvider(), context = coroutineContext) } // 同理修改其他日志级别扩展函数(d/w/e等) }
关键注意事项
- 方案一的Debug探针仅在Debug环境使用,生产环境务必关闭,避免性能开销和潜在安全风险
- 方案二要求所有日志调用必须在协程内部执行,若在普通线程调用则只会打印线程名称
- 两种方案最终都能输出类似
[AWT-EventQueue-0 @coroutine#40]的格式
内容的提问来源于stack exchange,提问作者Cindy
相关产品推荐
相关产品推荐

