用过Retrofit的朋友肯定知道使用Retrofit进行网络网络请求非常的方便简洁,但是要打印网络请求的日志还是要自己另想办法。昨天在网上找了一圈,发现要打印日志,大部分的帖子都是引入OkHttp3的日志库:

compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'

这个库使用起来很简单,在你创建OkHttpClient的时候增加拦截器即可

new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(15, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS).addInterceptor(new HttpLoggingInterceptor())// 在此处添加拦截器即可,默认日志级别为BASIC.build();

日志级别

日志的级别设置,日志级别包括:NONE(无日志),BASIC(基础日志),HEADERS(包含请求头),BODY(包含请求体)。默认的日志级别是BASIC,最大级别是BODY,会打印整个网络请求的所有信息,让我们来看一下效果:

// Request请求
--> POST http://app.buddha.net.cn/SzgServer/api/getRequestOrder http/1.1     // 请求行
Content-Type: application/x-www-form-urlencoded        // 请求头
Content-Length: 9       // 请求头长度
test=test       // 请求体
--> END POST (9-byte body)
// Response响应
<-- 200 OK http://app.buddha.net.cn/SzgServer/api/getRequestOrder (79ms)
Server: nginx/1.9.3
Date: Sat, 15 Jul 2017 04:00:29 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 38
Connection: keep-alive
Cache-Control: no-cache, must-revalidate
{"msg":"参数错误","status":"2005"}       // 响应结果
<-- END HTTP (38-byte body)

困惑

以上就是日志级别为BODY所打印出的信息,可以看到非常的全面,包含了Http请求的所有信息,但是!但是!作者并不满足于这种,我想让打印的日志更加直观,就比如说我想把json数据格式化输出,就像这样:

{"msg": "参数错误","status": "2005"
}

由于HttpLoggingInterceptor使用的是安卓自带的Logger来打印日志,所以是不具备把json数据格式化的功能的。这难免会让我们在开发中感到困惑,因为大部分网络请求数据都为json格式,我们希望打印出来的json数据是排版过的可以吗?当然可以,作者决定自己做点小修改,其实也很简单,我们只需要把Logger替换成我们自己的日志框架不就好了吗?现在作者修改后的日志打印如下,作者使用的是KLog,读者可以自行替换:

[ (HttpLoggingInterceptor.java:139)#intercept ] --> POST http://app.buddha.net.cn/SzgServer/api/getRequestOrder http/1.1
[ (HttpLoggingInterceptor.java:146)#intercept ] Content-Type: application/json; charset=UTF-8
[ (HttpLoggingInterceptor.java:149)#intercept ] Content-Length: 25
[ (HttpLoggingInterceptor.java:176)#intercept ]
╔═══════════════════════════════════════════════════════════════════════════════════════
║ [ (HttpLoggingInterceptor.java:178)#intercept ]
║ {
║     "age": 1,
║     "name": "liuwei"
║ }
╚═══════════════════════════════════════════════════════════════════════════════════════
[ (HttpLoggingInterceptor.java:179)#intercept ] --> END POST (25-byte body)
[ (HttpLoggingInterceptor.java:201)#intercept ] <-- 200 OK http://app.buddha.net.cn/SzgServer/api/getRequestOrder (35ms)
[ (HttpLoggingInterceptor.java:208)#intercept ] Server: nginx/1.9.3
[ (HttpLoggingInterceptor.java:208)#intercept ] Date: Sat, 15 Jul 2017 04:17:43 GMT
[ (HttpLoggingInterceptor.java:208)#intercept ] Content-Type: application/json;charset=UTF-8
[ (HttpLoggingInterceptor.java:208)#intercept ] Content-Length: 38
[ (HttpLoggingInterceptor.java:208)#intercept ] Connection: keep-alive
[ (HttpLoggingInterceptor.java:208)#intercept ] Cache-Control: no-cache, must-revalidate
[ (HttpLoggingInterceptor.java:233)#intercept ]
╔═══════════════════════════════════════════════════════════════════════════════════════
║ [ (HttpLoggingInterceptor.java:234)#intercept ]
║ {
║     "msg": "参数错误",
║     "status": "2005"
║ }
╚═══════════════════════════════════════════════════════════════════════════════════════
[ (HttpLoggingInterceptor.java:237)#intercept ] <-- END HTTP (38-byte body)

别的都没变,更换了一下日志框架就让日志输出的这么爽,json结果和requestBody都排版出来了,一眼明了,同样还可以设置日志的级别和自定义修改,美滋滋

自定义的HttpLoggingInterceptor

最后附上修改后的HttpLoggingInterceptor,读者可以自定义日志输出,自己决定打印哪些不打印哪些,很方便:

import com.socks.library.KLog;import java.io.EOFException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okhttp3.internal.platform.Platform;
import okio.Buffer;
import okio.BufferedSource;import static okhttp3.internal.platform.Platform.INFO;/*** 什么都没做,只是把Logger替换成KLog,读者可自行替换日志框架*/
public final class HttpLoggingInterceptor implements Interceptor {private static final Charset UTF8 = Charset.forName("UTF-8");public enum Level {/** No logs. */NONE,/*** Logs request and response lines.** <p>Example:* <pre>{@code* --> POST /greeting http/1.1 (3-byte body)** <-- 200 OK (22ms, 6-byte body)* }</pre>*/BASIC,/*** Logs request and response lines and their respective headers.** <p>Example:* <pre>{@code* --> POST /greeting http/1.1* Host: example.com* Content-Type: plain/text* Content-Length: 3* --> END POST** <-- 200 OK (22ms)* Content-Type: plain/text* Content-Length: 6* <-- END HTTP* }</pre>*/HEADERS,/*** Logs request and response lines and their respective headers and bodies (if present).** <p>Example:* <pre>{@code* --> POST /greeting http/1.1* Host: example.com* Content-Type: plain/text* Content-Length: 3** Hi?* --> END POST** <-- 200 OK (22ms)* Content-Type: plain/text* Content-Length: 6** Hello!* <-- END HTTP* }</pre>*/BODY}public interface Logger {void log(String message);/** A {@link Logger} defaults output appropriate for the current platform. */Logger DEFAULT = new Logger() {@Override public void log(String message) {Platform.get().log(INFO, message, null);}};}public HttpLoggingInterceptor() {}private volatile Level level = Level.NONE;/** Change the level at which this interceptor logs. */public HttpLoggingInterceptor setLevel(Level level) {if (level == null) throw new NullPointerException("level == null. Use Level.NONE instead.");this.level = level;return this;}public Level getLevel() {return level;}@Override public Response intercept(Chain chain) throws IOException {Level level = this.level;Request request = chain.request();if (level == Level.NONE) {return chain.proceed(request);}boolean logBody = level == Level.BODY;boolean logHeaders = logBody || level == Level.HEADERS;RequestBody requestBody = request.body();boolean hasRequestBody = requestBody != null;Connection connection = chain.connection();Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol;if (!logHeaders && hasRequestBody) {requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";}KLog.d(requestStartMessage);if (logHeaders) {if (hasRequestBody) {// Request body headers are only present when installed as a network interceptor. Force// them to be included (when available) so there values are known.if (requestBody.contentType() != null) {KLog.d("Content-Type: " + requestBody.contentType());}if (requestBody.contentLength() != -1) {KLog.d("Content-Length: " + requestBody.contentLength());}}Headers headers = request.headers();for (int i = 0, count = headers.size(); i < count; i++) {String name = headers.name(i);// Skip headers from the request body as they are explicitly logged above.if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {KLog.d(name + ": " + headers.value(i));}}if (!logBody || !hasRequestBody) {KLog.d("--> END " + request.method());} else if (bodyEncoded(request.headers())) {KLog.d("--> END " + request.method() + " (encoded body omitted)");} else {Buffer buffer = new Buffer();requestBody.writeTo(buffer);Charset charset = UTF8;MediaType contentType = requestBody.contentType();if (contentType != null) {charset = contentType.charset(UTF8);}KLog.d("");if (isPlaintext(buffer)) {KLog.json(buffer.readString(charset));KLog.d("--> END " + request.method()+ " (" + requestBody.contentLength() + "-byte body)");} else {KLog.d("--> END " + request.method() + " (binary "+ requestBody.contentLength() + "-byte body omitted)");}}}long startNs = System.nanoTime();Response response;try {response = chain.proceed(request);} catch (Exception e) {KLog.d("<-- HTTP FAILED: " + e);throw e;}long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);ResponseBody responseBody = response.body();long contentLength = responseBody.contentLength();String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";KLog.d("<-- " + response.code() + ' ' + response.message() + ' '+ response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", "+ bodySize + " body" : "") + ')');if (logHeaders) {Headers headers = response.headers();for (int i = 0, count = headers.size(); i < count; i++) {KLog.d(headers.name(i) + ": " + headers.value(i));}if (!logBody || !HttpHeaders.hasBody(response)) {KLog.d("<-- END HTTP");} else if (bodyEncoded(response.headers())) {KLog.d("<-- END HTTP (encoded body omitted)");} else {BufferedSource source = responseBody.source();source.request(Long.MAX_VALUE); // Buffer the entire body.Buffer buffer = source.buffer();Charset charset = UTF8;MediaType contentType = responseBody.contentType();if (contentType != null) {charset = contentType.charset(UTF8);}if (!isPlaintext(buffer)) {KLog.d("");KLog.d("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");return response;}if (contentLength != 0) {KLog.d("");KLog.json(buffer.clone().readString(charset));}KLog.d("<-- END HTTP (" + buffer.size() + "-byte body)");}}return response;}/*** Returns true if the body in question probably contains human readable text. Uses a small sample* of code points to detect unicode control characters commonly used in binary file signatures.*/static boolean isPlaintext(Buffer buffer) {try {Buffer prefix = new Buffer();long byteCount = buffer.size() < 64 ? buffer.size() : 64;buffer.copyTo(prefix, 0, byteCount);for (int i = 0; i < 16; i++) {if (prefix.exhausted()) {break;}int codePoint = prefix.readUtf8CodePoint();if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {return false;}}return true;} catch (EOFException e) {return false; // Truncated UTF-8 sequence.}}private boolean bodyEncoded(Headers headers) {String contentEncoding = headers.get("Content-Encoding");return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");}
}

Retrofit打印网络请求日志相关推荐

  1. Android 开发之Okhttp网络请求日志打印

    这里写自定义目录标题 Android 开发之Okhttp 网络请求日志打印 OkHTTP网络日志打印 Android 开发之Okhttp 网络请求日志打印 网络请求是开发的日常工作内容之一,网络日志打 ...

  2. android studio放置在函数上面看_Android中用Kotlin协程和Retrofit进行网络请求和取消请求...

    前面两篇文章介绍了协程的一些基本概念和基本知识,这篇则介绍在Android中如何使用协程配合Retrofit发起网络请求,同时介绍在使用协程时如何优雅的取消已经发起的网络请求. 需要文章中demo完整 ...

  3. Android中使用Kotlin协程(Coroutines)和Retrofit进行网络请求(二)之文件下载

    写在前面 下载功能是非常常用的功能,今天我们要通过kotlin协程和retrofit来是实现文件下载的功能.retorfit本身可以将请求结果以InputStream的形式返回,拿到InputStre ...

  4. android搭建网络框架,Android 搭建MVP+Retrofit+RxJava网络请求框架(三)

    上一篇中主要是将mvp+rxjava+retrofit进行了结合,本篇主要是对mvp框架的优化:建议先去看上一篇:Android 搭建MVP+Retrofit+RxJava网络请求框架(二) 针对vi ...

  5. 轻松搞定Retrofit不同网络请求方式的请求参数配置,及常用注解使用

    <一>四种请求方式: GET 向服务器发起数据请求,获取信息.类似于数据库的select操作,只是查询,不会影响资源的内容. POST 向服务器发送数据,该请求会改变数据的种类等资源.类似 ...

  6. Android 教你一步步搭建MVP+Retrofit+RxJava网络请求框架

    目录 1.什么是MVP? 2.什么是Retrofit? 3.RxJava 4.实践 之前公司的项目用到了MVP+Retrofit+RxJava的框架进行网络请求,所以今天特此写一篇文章以做总结.相信很 ...

  7. Retrofit+RxJava网络请求失败,报HTTP 400 Bad Request,没有返回errorBody的信息

    网络请求失败返回的结果肯定是到了onFaild()里面了,throwable获取的信息只有"HTTP 400 Bad Request",并没有返回postman上面的body信息. ...

  8. MVP+Retrofit+Rxjava网络请求购物车

    //依赖 compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.squareup.okhttp3:okhttp:3.9.0' com ...

  9. 使用Axios拦截器打印前端请求日志和后端后返回日志

    在main.ts引入 import axios from 'axios'; axios.defaults.baseURL = process.env.VUE_APP_SERVER;/*** axios ...

最新文章

  1. 关于GDPR的六大理解
  2. VTK:小部件之CheckerboardWidget
  3. oracle视图执行脚本,oracle 视图,函数,过程,触发器自动编译脚本
  4. 《深入浅出玩转FPGA》笔记
  5. smarty模板概念及应用场合
  6. 小程序导航组件navigator活学活用
  7. jvm感知docker容器参数
  8. React开发(283):控制文件不会被git追踪
  9. 【计算机网络】比较TCP与UDP
  10. 一休自评应聘:我是如何进入51CTO的?
  11. Android工程项目打包成SDK(jar或aar格式)
  12. mat opencv 修改roi_OpenCV开发笔记(七十三):红胖子8分钟带你使用opencv+dnn+yolov3识别物体...
  13. 电子学会2022年9月青少年软件编程(图形化)等级考试试卷(四级)答案解析
  14. 两种方法去除页眉页脚:基于OCR识别后的文本/基于图片切割
  15. Android TextView带背景图片和自定义边框
  16. N、XR、XD、DR”各代表什么意思
  17. 【MDT】MacBook Air 横评 MateBook 13
  18. java程序设计第四版张弛答案,2020学堂云Java编程概论——第一部分单元测试答案...
  19. Android闹钟APP
  20. WPF登录界面及程序主界面设计

热门文章

  1. linux php安装 phpize,linux下的phpize扩展安装实例详解
  2. 迭代法求sinx的近似值
  3. 96年创业者给你的建议
  4. 更改elementui主题颜色
  5. 每个人的成功都是一条辛酸的路
  6. linux内核读取u盘扇区,Linux下获取存储介质扇区大小
  7. 《kafka 核心技术与实战》课程学习笔记(五)
  8. 大数据智能推荐促进内容生态建设 今日头条与时尚集团战略合作
  9. 电商平台-优惠券设计
  10. CH573/CC2541 主机 扫描广播 通过UUID过滤设备 获取AD TYPE 数据