Android 开发之retrofit网络请求日志打印 可读性更强的日志打印 HttpLoggingInterceptor
网络请求是开发的日常工作内容之一,网络日志打印也有很多要注意及优化的部分,本文分享我在开发过程中编写的retrofit网络请求日志打印方法实现
retrofit网络日志打印
直接用OKHTTP请求接口的方式
- 要在接口请求的方法内添加拦截器进行信息获取和打印 ,即在自定义的HttpLoggingInterceptor实现类实现Interceptor接口
public class HttpLoggingInterceptor extends PrintLogUtils implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {}
}
-
关于PrintLogUtils工具类是用来打印收集的log信息集合,保证线程安全,增强打印可读性,详细源码请参考我之前的文章《Android 开发之Okhttp网络请求日志打印》。接下来展示intercept方法的内容
-
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; }
-
最后创建打印方法,由于网络请求不在同一个线程,为了避免打印的信息错乱,多个请求返回信息输出不对应,我们要用到synchronized关键字来保证线程安全,也就是依次执行。为了保证可读性,还添加了视图框
-
`
`/** * 打印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())
}
-
接下来看一下打印效果
-
到此 功能就写完了,我把源码放在一个Android开发分享的项目里,项目会陆续上传我自己写的多种工具类及框架,本文相关的 源码地址 http://www.hefan.space:1001/blob/share%2Fandroid.git/master/app%2Fsrc%2Fmain%2Fjava%2Fcom%2Frunt%2Fsharedcode%2Futils%2FHttpLoggingInterceptor.java
-
也可以加QQ群交流技术
最后推荐一个我自己写的MVVM开源项目《Open MVVM》
(想加扣扣讨论群请进入文章结尾查看群号)
有问题请私信,留言,或者发送邮件到我扣扣邮箱 qingingrunt2010