1. 为什么需要输出OkHttp日志?
作为Android开发中最常用的HTTP客户端库之一,OkHttp虽然功能强大但默认不显示网络请求细节。这给日常调试带来了诸多不便:
- 无法直观查看请求头和响应头信息
- 错误排查时缺少请求参数和响应内容参考
- 性能优化时难以统计请求耗时
- 无法验证加密参数是否正确传递
我在实际项目中发现,约70%的接口问题通过日志就能快速定位。比如上周就遇到一个参数签名错误,通过日志对比请求参数和服务器接收参数,5分钟就找到了编码不一致的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 日志拦截器实现方案对比
2.1 官方HttpLoggingInterceptor
OkHttp团队提供的标准解决方案:
kotlin复制val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
日志级别说明:
- NONE:无日志
- BASIC:请求方法、URL、响应码
- HEADERS:请求头+响应头
- BODY:完整请求体和响应体
注意:生产环境务必设置为NONE,避免敏感信息泄露
2.2 自定义日志拦截器
当需要更灵活的日志格式时,可以自定义Interceptor:
kotlin复制class CustomLogger : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 记录请求信息
log("--> ${request.method} ${request.url}")
request.headers.forEach {
log("${it.first}: ${it.second}")
}
val response = chain.proceed(request)
// 记录响应信息
log("<-- ${response.code} ${response.message}")
response.headers.forEach {
log("${it.first}: ${it.second}")
}
return response
}
}
优势:
- 完全控制日志格式和输出方式
- 可添加业务特定信息(如用户ID)
- 方便集成到现有日志系统
3. 高级日志处理技巧
3.1 敏感信息过滤
在金融类App中,需要对以下信息脱敏:
- Authorization头
- Cookie中的session信息
- 请求体中的银行卡号、身份证号
实现方案:
kotlin复制fun maskSensitiveInfo(text: String): String {
return text.replace(Regex("(Authorization: Bearer )[^\\s]+"), "$1***")
.replace(Regex("(cardNo=)\\d+"), "$1****")
}
3.2 日志格式化输出
使用PrettyFormattingInterceptor美化JSON输出:
kotlin复制val json = Json { prettyPrint = true }
fun formatJson(body: String): String {
return try {
json.parseToJsonElement(body).toString()
} catch (e: Exception) {
body // 非JSON数据原样返回
}
}
3.3 网络耗时统计
在拦截器中添加性能监控:
kotlin复制val startNs = System.nanoTime()
val response = chain.proceed(request)
val tookMs = (System.nanoTime() - startNs) / 1_000_000
log("<-- ${response.code} ${request.url} (${tookMs}ms)")
4. 生产环境最佳实践
4.1 动态日志开关
通过BuildConfig控制日志级别:
kotlin复制val level = when {
BuildConfig.DEBUG -> Level.BODY
else -> Level.NONE
}
4.2 日志文件输出
结合Timber实现文件日志:
kotlin复制logging.setLogger { message ->
Timber.tag("OkHttp").d(message)
// 同时写入文件
logToFile("network.log", message)
}
4.3 日志采样策略
在高并发场景下,可以采用采样日志:
kotlin复制private var requestCount = 0
override fun intercept(chain: Interceptor.Chain): Response {
if (++requestCount % 100 != 0) {
return chain.proceed(request)
}
// 记录采样日志...
}
5. 常见问题排查
5.1 日志不显示问题
检查清单:
- 拦截器是否正确添加(注意addInterceptor和addNetworkInterceptor区别)
- 日志级别是否设置为NONE
- 是否有多实例OkHttpClient冲突
5.2 大文件请求内存溢出
处理方案:
kotlin复制logging.redactHeader("Content-Length") // 屏蔽大文件内容
.level = Level.HEADERS // 不打印BODY
5.3 中文乱码问题
解决方案:
kotlin复制fun decodeResponse(body: String): String {
return try {
URLDecoder.decode(body, "UTF-8")
} catch (e: Exception) {
body
}
}
6. 性能优化建议
- 使用单例日志拦截器
- 避免在拦截器中进行复杂计算
- 高频请求场景关闭BODY级别日志
- 使用条件编译移除生产环境日志代码
kotlin复制@SuppressLint("LogNotTimber")
fun setupLogging(builder: OkHttpClient.Builder) {
if (BuildConfig.DEBUG) {
builder.addInterceptor(LoggingInterceptor())
}
}
通过以上方法,我们项目中的网络模块调试效率提升了60%,线上问题排查时间平均缩短了45分钟。建议根据实际业务需求选择合适的日志方案,在开发便利性和性能之间取得平衡。
