添加日志拦截器:用 HttpLoggingInterceptor 把网络请求“扒光”看
做网络优化,第一条铁律是什么?
我个人觉得,是“看得见”。
你连请求发出去啥样、返回啥样都看不见,那优化就成了闭着眼修车。所以这一章,咱们就聊怎么用 HttpLoggingInterceptor 把请求和响应的日志打印出来,看得清清楚楚。
为什么我需要这个拦截器?
你想想看,平时调接口,最常见的问题就是:
- 参数传错了没?
- Header 漏了 ?
- 服务器返回的 JSON 结构是不是变了?
- 明明该走的缓存,怎么又走了网络?
没有日志,你只能靠猜。有了日志,你一眼就能看出问题在哪。
我在项目中遇到过,有一次线上反馈某个页面数据一直是空的,查了半天才发现是后端改了字段名,但我没收到通知。要是日志早打出来了,十分钟就能定位。
快速上手:三步集成
好,不废话,直接上代码。首先添加依赖:
// build.gradle (app)
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
然后创建拦截器实例,加给 OkHttpClient:
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
// 设置日志级别
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build();
最后把 client 传给 Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.build();
这就够了。跑一下你的 App,所有请求和响应都会打印到 Logcat,Tag 默认是 OkHttp。
日志级别怎么选?一张表说清楚
| 级别 | 打印内容 | 适合场景 |
|---|---|---|
NONE | 什么都不打印 | Release 包禁用日志 |
BASIC | 请求方法、URL、响应码、耗时 | 只想看接口通不通 |
HEADERS | 请求/响应 Header + 基础信息 | 调试 Header 是否正确 |
BODY | 全部内容(Header+Body) | 详细调试,看参数和返回数据 |
我个人建议:
Debug 构建用
切记不要在生产环境打印 Body 日志,否则用户数据可能泄露。
Debug 构建用
BODY,Release 用 NONE。切记不要在生产环境打印 Body 日志,否则用户数据可能泄露。
灵魂拷问:日志打得太长怎么办?
我用 BODY 级别时经常遇到一个问题——响应体太大。
比如某个接口返回几百 K 的 JSON,Logcat 直接打印到屏幕溢出,甚至卡死。
嗯,这里要注意:Logcat 单条消息有限制(约 4KB),超过会被截断。
怎么破?
我一般用两种办法:
- 改用
HEADERS级别,外加自己写一个条件判断:只在需要时打印 Body。 - 自定义日志输出,把日志写到文件里,或者用分段打印。
下面是一个简单的分段打印工具:
public class HttpLogUtil {
public static void log(String tag, String message) {
int maxLength = 3000;
for (int i = 0; i < message.length(); i += maxLength) {
int end = Math.min(i + maxLength, message.length());
Log.d(tag, message.substring(i, end));
}
}
}
然后在 HttpLoggingInterceptor 的 Logger 里替换默认输出:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> {
HttpLogUtil.log("OkHttp", message);
});
logging.setLevel(Level.BODY);
避坑:敏感信息别往日志里打
我曾经踩过一个坑:
有次上线前忘了去掉日志拦截器,结果用户登录时的密码明文直接打印在 Logcat 里。虽然没人直接看手机日志,但要是被恶意应用读取了后果很严重。
所以记住:生产环境不能用
有次上线前忘了去掉日志拦截器,结果用户登录时的密码明文直接打印在 Logcat 里。虽然没人直接看手机日志,但要是被恶意应用读取了后果很严重。
所以记住:生产环境不能用
BODY 级别。
如果实在要调试,可以加一个“脱敏拦截器”,在打印前替换敏感字段:
public class SensitiveDataFilterInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 脱敏处理:替换密码字段
String body = request.body() != null ? request.body().toString() : "";
body = body.replaceAll("&password=[^&]+", "&password=***");
Log.d("SensitiveFilter", "Request body: " + body);
return chain.proceed(request);
}
}
知识体系结构图
下面这张图帮你理清日志拦截器的核心位置和作用:
实战中我还做了哪些改造?
你可能会问,默认的 HttpLoggingInterceptor 够用吗?
说实话,大多数场景够了。但我个人习惯做三件事:
- 统一日志标签:把 Tag 改成
"HTTP"而不是默认的"OkHttp",方便 Logcat 过滤。 - 加上时间戳:在拦截器里记下请求开始时间和结束时间,方便做性能分析。
- 条件打印:只在 Debug 且网络状态为 WIFI 时才打完整 Body,移动网络下只打 Header。
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override public void log(String message) {
if (BuildConfig.DEBUG) {
Log.d("HTTP", message);
}
}
});
logging.setLevel(BuildConfig.DEBUG ? Level.BODY : Level.NONE);
这种做法,既能保证开发效率,又能守住安全底线。
总结一句话
日志拦截器是你的“网络摄像头”。
调好它,就等于给每个接口装了监控。
多用它,少加班。