建立Okhttp自定义Log

原文连接:建立OkHttp自定义Loghtml

背景

本文重点讲解如何在使用OkHttp做为网络请求框架的前提下,如何自定义一个适合本身项目的Http Log,从而提高网络Api开发、调试效率。java

Http协议

只有对Http协议有基本的了解,才能更好的调试网络接口。git

一个故事

男女主一次偶然的机会,开始信件传情,但他们的信件并非直接寄给对方,而是先寄到某个地点A,由地点A的主人转发。(好吧,这是《北京赶上西雅图之不二情书》的情节)。咱们来看男主发信的过程:github

  1. 先购买有效邮票;
  2. 填写信件的收件人、收件地址等信息;
  3. 把信件放到信封里寄出去。

显然,女主收到信后回复信件也是一样的流程。json

类比

咱们如今将Http协议的消息结构和故事主人公收发信的过程作一个类比:
api

类比图

下面是使用Fiddler工具抓取www.xitu.io的网络请求和响应图:
稀土网络响应图

Http消息结构

由前面的分析可知,Http请求消息由三部分组成:服务器

  1. 请求行由3部分组成:①请求的方法,POST仍是GET等;②请求路径;③Http协议
  2. 请求头,每一行都是name:value的结构,包含各类来自请求客服端的信息
  3. 请求体,提交给服务器的信息,GET方法没有此项。

Http响应体跟请求体格式大体同样。网络

  1. 请求体有协议和响应码组成,200为响应成功
  2. 响应头,每一行都是name:value的结构,包含各类来自服务器的信息
  3. 响应体,返回客户端须要的数据

自定义LogInterceptor

OkHttp Interceptor

OkHttp的一大特色就是能够在发出请求体和收到响应体之间添加任意个数任意任意功能的拦截器,对请求体或者响应体进行操做。仍是用《北京赶上西雅图之不二情书》的故事来讲,那么地点A的主人就是充当拦截器的角色,在故事中他不只转发信件,还阅读了信件的内容。session

根据需求肯定须要Log的信息

Log的信息和划分两类,一类是跟业务相关的信息,一类是与业务无关。app

  1. 业务相关包括:
    请求地址url;
    请求头:token、sessionId;
    请求体:POST提交的内容;
    响应头:token/sessionId;
    响应体:服务器返回数据;

  2. 业务无关包括:
    网络状态码:200为正常反应;
    网络请求时间:从发出网络请求到响应所消耗的时间;
    网络协议:http一、http2等;
    网络Method:POST、GET等;
    不论是业务相关的数据仍是业务无关的数据,都是来自于Http请求体和响应体的消息结构中。

Log效果图

以公司项目的测试服务器自动登陆接口,log效果以下:

POST
acid->1075
userId->-1
network code->200
url->http://mobileapi.app100688440.twsapp.com/app/open/open.do?ACID=1075&userId=-1&vendorId=7999&VERS=6.5.1&fromType=1110
time->84.473
request headers->sessionId: 
request->{"data":{"pagenum":0,"uid":-1,"flag":"00000000"},"requeststamp":"20161212151841836466"}
body->{"code":200,"responsestamp":"20161212151841836466","data":{"uid":-1,"nickname":"游客258","uploadUrl":"http://mobileapi.app100688440.twsapp.com/uploadservlet","wxUrl":"http://wx.app100688440.twsapp.com","isEmcee":0,"canLive":0,"goodnum":0,"index":{},"revGift":{},"msgRemind":{},"freshmanMission":{},"account":{},"onlineLimit":"600","userSecretKey":"brjefjzw37ocw46"}}复制代码

log解释:
POST:此接口使用POST方法;
acid:标识此接口的id,每一个接口有惟一的acid,根据acid可查询到此接口的功能,例如此接口acid = 1075为自动登陆接口;
userId:用户id
network code:返回200证实服务器响应成功;
url:此接口请求的url地址;
time:为响应时间,若是某接口响应时间过长,排除网络环境的缘由,就能够跟服务端商量是否可优化;
request header:此项目须要传sessionId;
request:此处打印Post方法的请求体,若后台返回参数错误,检查此行log便可;
body:后台数据返回,采用json格式;

代码实现

/** * 添加Log */
public class LogInterceptor implements Interceptor {

    private static final String TAG = "LogInterceptor";
    private static final Charset UTF8 = Charset.forName("UTF-8"); //urf8编码

    @Override
    public Response intercept(Chain chain) throws IOException {  //实现Interceptor接口方法
        Log.d(TAG,"before chain,request()");
        Request request = chain.request();  //获取request
        String acid = request.url().queryParameter("ACID"); //在url中获取ACID的参数值;
        Response response;
        try {
            long t1 = System.nanoTime();
            response = chain.proceed(request); //OkHttp链式调用
            long t2 = System.nanoTime();
            double time = (t2 - t1) / 1e6d;   //用请求后的时间减去请求前的时间获得耗时

            String userId = request.url().queryParameter("userId");
            String type = "";
            if (request.method().equals("GET")) {    //判断Method类型
                type = "GET";
            } else if (request.method().equals("POST")) {
                type = "POST";
            } else if (request.method().equals("PUT")) {
                type = "PUT";
            } else if (request.method().equals("DELETE")) {
                type = "DELETE";
            }
            BufferedSource source = response.body().source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            Buffer buffer = source.buffer();
            String logStr = "\n--------------------".concat(TextUtils.isEmpty(acid) ? "" : acid).concat(" begin--------------------\n")
                    .concat(type)
                    .concat("\nacid->").concat(TextUtils.isEmpty(acid) ? "" : acid)
                    .concat("\nuserId->").concat(TextUtils.isEmpty(userId) ? "" : userId)
                    .concat("\nnetwork code->").concat(response.code() + "")
                    .concat("\nurl->").concat(request.url() + "")
                    .concat("\ntime->").concat(time + "")
                    .concat("\nrequest headers->").concat(request.headers() + "")
                    .concat("request->").concat(bodyToString(request.body()))
                    .concat("\nbody->").concat(buffer.clone().readString(UTF8)); //响应体转String
            Log.i(TAG, logStr);
        } catch (Exception e) {
            Log.d(TAG,e.getClass().toString()+", error:acid = "+acid);  //网络出错,log 出错的acid
            throw e; //不拦截exception,由上层处理网络错误
        }
        return response;
    }

    /** * 请求体转String * @param request * @return */
    private static String bodyToString(final RequestBody request) {

        try {
            final Buffer buffer = new Buffer();
            request.writeTo(buffer);
            return buffer.readUtf8();
        } catch (final IOException e) {
            return "did not work";
        }
    }

}复制代码

开源项目okhttp-logging-interceptor

若是不想本身编写代码,也可使用开源项目okhttp-logging-interceptor,支持:1.设置Log的等级;2.使用自定义Log。此开源项目也仅是一个Interceptor。但我的以为log的样式并不适合调试使用。同时log的内容比较通用,若想突出对应项目的信息,建议仍是自定义Http Log。

Okhttp+Retrofit+Rxjava

此Interceptor配合OkHttp使用,关于Okhttp+Retrofit+Rxjava的整合,可查看RxJava+Retrofit+OkHttp封装

引用

okhttp-logging-interceptor
okhttp Interceptors
Http协议详解

相关文章
相关标签/搜索