用过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. 区块链开源实现fabric快速部署及CLI体验
  2. 缓冲区溢出(buffer overflow)机理分析
  3. 《操作系统》库函数与系统调用的区别和联系?
  4. linux下raid5的配置
  5. c语言字符串字节数函数,C语言字符,字符串,字节操作常用函数
  6. python怎么创建类的例题_Python_面向对象练习题
  7. Python3 Time 模块详解 import time
  8. [ZJOI2012]灾难(建图)
  9. mysql etc my.cnf_mysql配置文件/etc/my.cnf
  10. php添加gd扩展,php安装gd扩展的方法
  11. [iOS]仿微博视频边下边播之封装播放器
  12. Web端让电脑说话,发音,类似闹钟
  13. 写给程序员的 2018 新年计划清单
  14. 一招教你如何在简历上突出工作经验!(干货)
  15. NX二次开发-UFUN获取part的单位(公制/英制)UF_PART_ask_units
  16. Apple 软件产品使用的 TCP 和 UDP 端口
  17. 【C#】关闭 Window 之后,无法设置 Visibility,也无法调用 Show、ShowDialogor 或 WindowInteropHelper.EnsureHandle
  18. Tic-Tac-Toe(三井旗)
  19. 叶胜超:IOST ----基于分片技术的第四大公链项目!
  20. Signature|privileged permissions not in privapp-permissions whitelist异常处理

热门文章

  1. php emmet,Emmet插件使用方法总结
  2. python手机中文版下载_Python编程下载_Python中文版下载v2.7.18_360手机助手专区
  3. XenServer 5.5 断电重启虚拟机磁盘丢失的修复
  4. mongo存储引擎那些事(一):硬盘数据结构
  5. pytest——03 setup和teardown
  6. gitkraen_GitKraken中文版
  7. SSM校园商铺项目1.0总结
  8. 《大数据建模、分析、挖掘技术》
  9. 【疑难杂症】360主页锁定解决办法
  10. 刚刚整理的截获SQL错误代码弹出提示信息类.