Android 开发之retrofit网络请求日志打印 可读性更强的日志打印 HttpLoggingInterceptor

Android 开发之retrofit网络请求日志打印 可读性更强的日志打印 HttpLoggingInterceptor

网络请求是开发的日常工作内容之一,网络日志打印也有很多要注意及优化的部分,本文分享我在开发过程中编写的retrofit网络请求日志打印方法实现

retrofit网络日志打印

直接用OKHTTP请求接口的方式

  1. 要在接口请求的方法内添加拦截器进行信息获取和打印 ,即在自定义的HttpLoggingInterceptor实现类实现Interceptor接口
 public  class HttpLoggingInterceptor extends PrintLogUtils implements Interceptor {
	 	@Override
	   	 public Response intercept(Chain chain) throws IOException {}
	}
  1. 关于PrintLogUtils工具类是用来打印收集的log信息集合,保证线程安全,增强打印可读性,详细源码请参考我之前的文章《Android 开发之Okhttp网络请求日志打印》。接下来展示intercept方法的内容

  2. public Response intercept(Chain chain) throws IOException {
    
        Request request = chain.request()
                .newBuilder()
                //.addHeader("token", UserManager.getInstance().getUserToken())添加了此方法则覆盖了请求头,需要在此再次添加
                .build();
    
        ArrayList<String> logArrays = new ArrayList<>();
        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() + ' ' + URLDecoder.decode(request.url().toString() ,"UTF-8")+ ' ' + protocol;
        if ( hasRequestBody) {
            requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
        }
        logArrays.add(requestStartMessage);
    
        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) {
                logArrays.add("Content-Type: " + requestBody.contentType());
            }
            if (requestBody.contentLength() != -1) {
                logArrays.add("Content-Length: " + requestBody.contentLength());
            }
        }
    
        Headers headers = request.headers();
        logArrays.add("---------->REQUEST HEADER<----------");
        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)) {
                logArrays.add(name + ": " + headers.value(i));
            }
        }
    
        if (!hasRequestBody) {
            logArrays.add("--> END " + request.method());
        } else if (bodyEncoded(request.headers())) {
            logArrays.add("--> 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);
            }
            if (isPlaintext(buffer)) {
                logArrays.add("---------->REQUEST BODY<----------");
    
                logArrays.add(URLDecoder.decode(buffer.readString(charset), "UTF-8"));
                logArrays.add("--> END " + request.method()
                        + " (" + requestBody.contentLength() + "-byte body)");
            } else {
                logArrays.add("--> END " + request.method() + " (binary "
                        + requestBody.contentLength() + "-byte body omitted)");
            }
        }
        long startNs = System.nanoTime();
        Response response;
        try {
            response = chain.proceed(request);
        } catch (Exception e) {
            logArrays.add("<-- 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";
        logArrays.add("<-- response code:" + response.code() + " message:" + response.message()+" contentlength:"+bodySize  );
        logArrays.add("<-- response url:"+ URLDecoder.decode(response.request().url().toString(),"UTF-8")  );
        logArrays.add("<-- costtimes :  (" + tookMs + "ms"  + ')');
    
    
        if ( !HttpHeaders.hasBody(response)) {
            logArrays.add("<-- END HTTP");
        } else if (bodyEncoded(response.headers())) {
            logArrays.add("<-- 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)) {
                logArrays.add("");
                logArrays.add("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
                return response;
            }
    
            logArrays.add("---------->RESPONSE BODY<----------");
            if (contentLength != 0) {
                logArrays.add(buffer.clone().readString(charset));
            }
    
            logArrays.add("<-- END HTTP (" + buffer.size() + "-byte body)");
        }
        new Thread(){
            @Override
            public void run() {
                printLog(logArrays);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应
            }
        }.start();
        return response;
    }
    
  3. 最后创建打印方法,由于网络请求不在同一个线程,为了避免打印的信息错乱,多个请求返回信息输出不对应,我们要用到synchronized关键字来保证线程安全,也就是依次执行。为了保证可读性,还添加了视图框

  4. `

      `/**
     * 打印log
     * @param list
     */
    private synchronized void printLog(ArrayList<String> list,boolean info){
      int length = 0 ;//计算每行最长的长度
        StringBuilder sb = new StringBuilder();
        for(String str : list){
            if(str.indexOf("\n")>-1){//有换行的拆分处理
                String[] split = str.split("\n");
                for(String s : split){
                    s = s.replace("\t","    ");//缩进替换空格
                    if(length<s.length()){
                        length = s.length();
                    }
                }
            }else{
                if(length<str.length()){
                    length = str.length();
                }
            }
        }
        length+=14;//左右间距
        String head = "HTTP  REQUEST START";
        sb.append(" \n\n\n"+"\n");
        //打印头部
        String logHead = "┏"+getEmptyStr((length-head.length())/2,"━")+head+getEmptyStr((length-head.length())/2,"━")+"┓";
        sb.append(logHead+"\n");
        //打印内容
        for(String str : list){
            String logStr = "";
            if(str.indexOf("\n")>-1){//内部换行替换
                splitStr(str,logHead.length(),sb);
            }else{
                if(str.length()> logHead.length()){
                    outOflength(str,logHead.length(),sb);
                }else {
                    logStr = "┃      " + str + getEmptyStr((length - 14 - str.length()), " ");
                    //处理中文间距,保证打印无偏差
                    sb.append(logStr + getEmptyStr((logHead.length() - logStr.length() - 1 - hasCNchar(logStr)), " ") + "┃ \n");
                }
            }
        }
        String end = "HTTP  REQUEST END";
        //打印结尾
        sb.append("┗"+getEmptyStr((length-end.length())/2,"━")+end+getEmptyStr((length-end.length())/2,"━")+"┛\n");
        sb.append(" \n\n\n");
        //Logger.DEFAULT.log(sb.toString());//打印log,避免多个log语句,导致log输出时其他线程的log输出切入此输出阵列内
        String[] split = sb.toString().split("\n");
        for(String str : split){
            if(info) {
                MyLog.i(TAG, str);
            }else{
                MyLog.e(TAG, str);
            }
        }
    }`
    

该拦截器的使用方法,因为retrofit依然是基于OKhttp去请求的,所以需要在Okhttp对象中添加拦截监听

    OkHttpClient.Builder client = new OkHttpClient.Builder()
            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)//设置超时时间
            .retryOnConnectionFailure(true);

    //添加拦截
    if (BuildConfig.DEBUG) {
        client.addInterceptor(new HttpLoggingInterceptor())
    }

  1. 接下来看一下打印效果
    在这里插入图片描述

  2. 到此 功能就写完了,我把源码放在一个Android开发分享的项目里,项目会陆续上传我自己写的多种工具类及框架,本文相关的 源码地址 http://www.hefan.space:1001/blob/share%2Fandroid.git/master/app%2Fsrc%2Fmain%2Fjava%2Fcom%2Frunt%2Fsharedcode%2Futils%2FHttpLoggingInterceptor.java

  3. 也可以加QQ群交流技术

最后推荐一个我自己写的MVVM开源项目《Open MVVM》
(想加扣扣讨论群请进入文章结尾查看群号)

有问题请私信,留言,或者发送邮件到我扣扣邮箱 qingingrunt2010

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值