添加日志拦截器:用 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,Release 用 NONE。
切记不要在生产环境打印 Body 日志,否则用户数据可能泄露。

灵魂拷问:日志打得太长怎么办?

我用 BODY 级别时经常遇到一个问题——响应体太大。
比如某个接口返回几百 K 的 JSON,Logcat 直接打印到屏幕溢出,甚至卡死。
嗯,这里要注意:Logcat 单条消息有限制(约 4KB),超过会被截断。

怎么破?
我一般用两种办法:

  1. 改用 HEADERS 级别,外加自己写一个条件判断:只在需要时打印 Body。
  2. 自定义日志输出,把日志写到文件里,或者用分段打印。

下面是一个简单的分段打印工具:

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 里。虽然没人直接看手机日志,但要是被恶意应用读取了后果很严重。
所以记住:生产环境不能用 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 日志拦截器知识体系 Android 应用 OkHttpClient HttpLoggingInterceptor 拦截请求(打印参数) 拦截响应(打印Body) Logcat / 文件输出 级别选择:NONE / BASIC / HEADERS / BODY 注意:生产环境禁用 BODY,防止敏感信息泄露

实战中我还做了哪些改造?

你可能会问,默认的 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);

这种做法,既能保证开发效率,又能守住安全底线。

总结一句话

日志拦截器是你的“网络摄像头”。
调好它,就等于给每个接口装了监控。
多用它,少加班。