Appearance
Logcat 调试技巧
字数统计:约 6000 字
难度等级:⭐⭐
面试重要度:⭐⭐⭐
目录
1. Logcat 基础
1.1 什么是 Logcat
Logcat 是 Android 的日志系统:
- 记录系统和应用日志
- 实时查看应用行为
- 调试和排查问题
- 性能分析1.2 查看 Logcat
bash
# Android Studio
# View → Tool Windows → Logcat
# 命令行
adb logcat
# 实时查看
adb logcat -v time
# 保存日志
adb logcat -d > log.txt
# 清空日志
adb logcat -c2. 日志级别
2.1 日志级别说明
kotlin
import android.util.Log
class LoggingExample {
companion object {
private const val TAG = "MyApp"
}
// Verbose - 最详细,生产环境关闭
fun verbose() {
Log.v(TAG, "Verbose message")
}
// Debug - 调试信息,生产环境关闭
fun debug() {
Log.d(TAG, "Debug message")
}
// Info - 一般信息
fun info() {
Log.i(TAG, "Info message")
}
// Warning - 警告,可能有问题
fun warning() {
Log.w(TAG, "Warning message")
}
// Error - 错误,需要关注
fun error() {
Log.e(TAG, "Error message", Exception("Stack trace"))
}
// Wtf - What a Terrible Failure,严重错误
fun wtf() {
Log.wtf(TAG, "This should never happen")
}
}2.2 日志级别过滤
bash
# 只显示 Error 及以上
adb logcat *:E
# 只显示 Warning 及以上
adb logcat *:W
# 只显示特定 TAG
adb logcat MyApp:I
# 多个 TAG
adb logcat MyApp:I OtherApp:D *:S
# 显示所有
adb logcat *:V3. 过滤与搜索
3.1 按 TAG 过滤
bash
# 单个 TAG
adb logcat -s "MyApp"
# 多个 TAG
adb logcat -s "MyApp" "OtherApp"
# TAG + 级别
adb logcat MyApp:I OtherApp:D *:S3.2 按进程过滤
bash
# 获取进程 PID
adb shell ps | grep com.example.app
# 按 PID 过滤
adb logcat --pid=1234
# 按包名过滤
adb logcat --pid=$(adb shell pidof com.example.app)3.3 按时间过滤
bash
# 显示时间戳
adb logcat -v time
# 显示日期时间
adb logcat -v threadtime
# 显示可机器读取的时间
adb logcat -v threadtime
# 只显示最近 100 行
adb logcat -t 100
# 只显示最近 1 分钟
adb logcat -T "01-01 12:00:00.000"3.4 关键词搜索
bash
# 包含关键词
adb logcat | grep "NullPointerException"
# 排除关键词
adb logcat | grep -v "Choreographer"
# 正则匹配
adb logcat | grep -E "Error|Exception"
# 高亮显示
adb logcat | grep --color=always "Error"4. 实战技巧
4.1 崩溃排查
bash
# 查找崩溃
adb logcat | grep "FATAL"
# 查找异常
adb logcat | grep "Exception"
# 查找 ANR
adb logcat | grep "ANR"
# 查看崩溃堆栈
adb logcat -d | grep -A 50 "Caused by"kotlin
// 捕获未处理异常
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
Log.e("CrashHandler", "Uncaught exception", throwable)
// 保存崩溃日志
saveCrashLog(throwable)
}
}
private fun saveCrashLog(throwable: Throwable) {
val writer = PrintWriter(File(cacheDir, "crash.log"))
throwable.printStackTrace(writer)
writer.close()
}
}4.2 网络调试
kotlin
// OkHttp 日志拦截器
class NetworkLogging {
private val loggingInterceptor = HttpLoggingInterceptor { message ->
Log.d("OkHttp", message)
}.apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val client = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
}bash
# 查看网络日志
adb logcat -s "OkHttp"
# 查看 HTTP 错误
adb logcat | grep "HTTP"4.3 性能分析
kotlin
// 性能日志
class PerformanceLogging {
fun measureTime(tag: String, block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val duration = System.currentTimeMillis() - start
Log.d(tag, "Execution time: ${duration}ms")
}
fun trace(tag: String, message: String) {
Trace.beginSection(message)
try {
// 代码
} finally {
Trace.endSection()
}
}
}bash
# 查看性能日志
adb logcat -s "Performance"
# 使用 Systrace
adb shell am set-debug-app --persistent com.example.app4.4 数据库调试
kotlin
// Room 查询日志
class DatabaseLogging {
private val queryCallback = RoomDatabase.QueryCallback { sql, bindArgs ->
Log.d("Database", "SQL: $sql, Args: ${bindArgs.joinToString()}")
}
// 在 Room 配置中启用
// .addQueryCallback(queryCallback, Executors.newSingleThreadExecutor())
}4.5 协程调试
kotlin
// 协程日志
class CoroutineLogging {
suspend fun debugCoroutine() {
withContext(Dispatchers.Default) {
Log.d("Coroutine", "Running on ${Thread.currentThread().name}")
// 代码
}
}
// 使用 CoroutineExceptionHandler
val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
Log.e("Coroutine", "Coroutine failed", throwable)
}
fun launchWithLogging() {
CoroutineScope(exceptionHandler).launch {
// 代码
}
}
}5. 最佳实践
5.1 日志规范
kotlin
class LoggingBestPractices {
companion object {
// ✅ 使用常量 TAG
private const val TAG = "MyClass"
// ❌ 不要每次创建
// private val TAG = javaClass.simpleName // 性能开销
}
// ✅ 包含上下文信息
fun loadData(userId: String) {
Log.d(TAG, "Loading data for user: $userId")
// 代码
}
// ✅ 记录异常堆栈
fun handleError(error: Throwable) {
Log.e(TAG, "Error occurred", error)
}
// ✅ 避免敏感信息
fun login(password: String) {
// ❌ 不要记录密码
// Log.d(TAG, "Password: $password")
// ✅ 记录脱敏信息
Log.d(TAG, "Login attempt for user")
}
// ✅ 生产环境关闭调试日志
fun debug(message: String) {
if (BuildConfig.DEBUG) {
Log.d(TAG, message)
}
}
}5.2 日志工具类
kotlin
object Logger {
private const val TAG_PREFIX = "MyApp_"
fun d(tag: String, message: String) {
if (BuildConfig.DEBUG) {
Log.d("$TAG_PREFIX$tag", message)
}
}
fun e(tag: String, message: String, throwable: Throwable? = null) {
Log.e("$TAG_PREFIX$tag", message, throwable)
}
fun i(tag: String, message: String) {
Log.i("$TAG_PREFIX$tag", message)
}
fun w(tag: String, message: String) {
Log.w("$TAG_PREFIX$tag", message)
}
// 带类名的快捷方法
inline fun <reified T> T.debug(message: String) {
Logger.d(T::class.java.simpleName, message)
}
inline fun <reified T> T.error(message: String, throwable: Throwable? = null) {
Logger.e(T::class.java.simpleName, message, throwable)
}
}
// 使用
class MyClass {
fun loadData() {
this.debug("Loading data")
// 代码
}
}5.3 日志性能
kotlin
class LoggingPerformance {
// ✅ 避免字符串拼接
fun efficientLog(value: Int) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Value: $value")
}
}
// ❌ 避免昂贵操作
fun inefficientLog() {
// 即使不显示也会执行
Log.d(TAG, "Data: ${expensiveOperation()}")
}
private fun expensiveOperation(): String {
// 耗时操作
return "data"
}
}6. 面试考点
6.1 基础概念
Q1: Logcat 的作用是什么?
答案要点:
- 查看系统和应用日志
- 调试和排查问题
- 性能分析
- 崩溃调查Q2: 日志级别有哪些?
答案要点:
- Verbose (V) - 最详细
- Debug (D) - 调试
- Info (I) - 一般信息
- Warning (W) - 警告
- Error (E) - 错误
- Wtf (WTF) - 严重错误6.2 实战问题
Q3: 如何查找崩溃日志?
bash
# 查找崩溃
adb logcat | grep "FATAL"
# 查找异常
adb logcat | grep "Exception"
# 查看堆栈
adb logcat -d | grep -A 50 "Caused by"Q4: 如何优化日志性能?
kotlin
// 使用 isLoggable 检查
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, message)
}
// 生产环境关闭调试日志
if (BuildConfig.DEBUG) {
Log.d(TAG, message)
}参考资料
本文完,感谢阅读!