From 7e2d7661cd01039ef49aa9c7693a37a49094d1bb Mon Sep 17 00:00:00 2001
From: Zweihui <616505546@qq.com>
Date: Wed, 31 Jan 2018 17:00:45 +0800
Subject: [PATCH] improve http print log
---
.../com/jess/arms/http/FormatPrinter.java | 236 ++++++++++++++++++
.../jess/arms/http/RequestInterceptor.java | 157 ++++++------
2 files changed, 322 insertions(+), 71 deletions(-)
create mode 100644 arms/src/main/java/com/jess/arms/http/FormatPrinter.java
diff --git a/arms/src/main/java/com/jess/arms/http/FormatPrinter.java b/arms/src/main/java/com/jess/arms/http/FormatPrinter.java
new file mode 100644
index 0000000..6c0c2fa
--- /dev/null
+++ b/arms/src/main/java/com/jess/arms/http/FormatPrinter.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2018 JessYan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jess.arms.http;
+
+import android.text.TextUtils;
+
+import java.util.List;
+
+import okhttp3.Request;
+import timber.log.Timber;
+
+/**
+ * ================================================
+ * 对 OkHttp 的请求和响应信息进行更规范和清晰的打印
+ *
+ * Created by JessYan on 25/01/2018 14:51
+ * Contact me
+ * Follow me
+ * ================================================
+ */
+
+public class FormatPrinter {
+ private static final String TAG = "ArmsHttpLog";
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+ private static final String DOUBLE_SEPARATOR = LINE_SEPARATOR + LINE_SEPARATOR;
+
+ private static final String[] OMITTED_RESPONSE = {LINE_SEPARATOR, "Omitted response body"};
+ private static final String[] OMITTED_REQUEST = {LINE_SEPARATOR, "Omitted request body"};
+
+ private static final String N = "\n";
+ private static final String T = "\t";
+ private static final String REQUEST_UP_LINE = "┌────── Request ────────────────────────────────────────────────────────────────────────";
+ private static final String END_LINE = "└───────────────────────────────────────────────────────────────────────────────────────";
+ private static final String RESPONSE_UP_LINE = "┌────── Response ───────────────────────────────────────────────────────────────────────";
+ private static final String BODY_TAG = "Body:";
+ private static final String URL_TAG = "URL: ";
+ private static final String METHOD_TAG = "Method: @";
+ private static final String HEADERS_TAG = "Headers:";
+ private static final String STATUS_CODE_TAG = "Status Code: ";
+ private static final String RECEIVED_TAG = "Received in: ";
+ private static final String CORNER_UP = "┌ ";
+ private static final String CORNER_BOTTOM = "└ ";
+ private static final String CENTER_LINE = "├ ";
+ private static final String DEFAULT_LINE = "│ ";
+
+ private FormatPrinter() {
+ throw new UnsupportedOperationException("you can't instantiate me!");
+ }
+
+ private static boolean isEmpty(String line) {
+ return TextUtils.isEmpty(line) || N.equals(line) || T.equals(line) || TextUtils.isEmpty(line.trim());
+ }
+
+ /**
+ * 打印网络请求信息, 当网络请求时 {{@link okhttp3.RequestBody}} 可以解析的情况
+ *
+ * @param request
+ * @param bodyString
+ */
+ static void printJsonRequest(Request request, String bodyString) {
+ final String requestBody = LINE_SEPARATOR + BODY_TAG + LINE_SEPARATOR + bodyString;
+ final String tag = getTag(true);
+
+ Timber.tag(tag).i(REQUEST_UP_LINE);
+ logLines(tag, new String[]{URL_TAG + request.url()}, false);
+ logLines(tag, getRequest(request), true);
+ logLines(tag, requestBody.split(LINE_SEPARATOR), true);
+ Timber.tag(tag).i(END_LINE);
+ }
+
+ /**
+ * 打印网络请求信息, 当网络请求时 {{@link okhttp3.RequestBody}} 为 {@code null} 或不可解析的情况
+ *
+ * @param request
+ */
+ static void printFileRequest(Request request) {
+ final String tag = getTag(true);
+
+ Timber.tag(tag).i(REQUEST_UP_LINE);
+ logLines(tag, new String[]{URL_TAG + request.url()}, false);
+ logLines(tag, getRequest(request), true);
+ logLines(tag, OMITTED_REQUEST, true);
+ Timber.tag(tag).i(END_LINE);
+ }
+
+ /**
+ * 打印网络响应信息, 当网络响应时 {{@link okhttp3.ResponseBody}} 可以解析的情况
+ *
+ * @param chainMs
+ * @param isSuccessful
+ * @param code
+ * @param headers
+ * @param bodyString
+ * @param segments
+ * @param message
+ * @param responseUrl
+ */
+ static void printJsonResponse(long chainMs, boolean isSuccessful,
+ int code, String headers, String bodyString, List segments, String message, final String responseUrl) {
+
+ final String responseBody = LINE_SEPARATOR + BODY_TAG + LINE_SEPARATOR + bodyString;
+ final String tag = getTag(false);
+ final String[] urlLine = {URL_TAG + responseUrl, N};
+
+ Timber.tag(tag).i(RESPONSE_UP_LINE);
+ logLines(tag, urlLine, true);
+ logLines(tag, getResponse(headers, chainMs, code, isSuccessful, segments, message), true);
+ logLines(tag, responseBody.split(LINE_SEPARATOR), true);
+ Timber.tag(tag).i(END_LINE);
+ }
+
+ /**
+ * 打印网络响应信息, 当网络响应时 {{@link okhttp3.ResponseBody}} 为 {@code null} 或不可解析的情况
+ *
+ * @param chainMs
+ * @param isSuccessful
+ * @param code
+ * @param headers
+ * @param segments
+ * @param message
+ * @param responseUrl
+ */
+ static void printFileResponse(long chainMs, boolean isSuccessful,
+ int code, String headers, List segments, String message, final String responseUrl) {
+ final String tag = getTag(false);
+ final String[] urlLine = {URL_TAG + responseUrl, N};
+
+ Timber.tag(tag).i(RESPONSE_UP_LINE);
+ logLines(tag, urlLine, true);
+ logLines(tag, getResponse(headers, chainMs, code, isSuccessful, segments, message), true);
+ logLines(tag, OMITTED_RESPONSE, true);
+ Timber.tag(tag).i(END_LINE);
+ }
+
+
+ /**
+ * 对 {@code lines} 中的信息进行逐行打印
+ *
+ * @param tag
+ * @param lines
+ * @param withLineSize 为 {@code true} 时, 每行的信息长度不会超过110, 超过则自动换行
+ */
+ private static void logLines(String tag, String[] lines, boolean withLineSize) {
+ for (String line : lines) {
+ int lineLength = line.length();
+ int MAX_LONG_SIZE = withLineSize ? 110 : lineLength;
+ for (int i = 0; i <= lineLength / MAX_LONG_SIZE; i++) {
+ int start = i * MAX_LONG_SIZE;
+ int end = (i + 1) * MAX_LONG_SIZE;
+ end = end > line.length() ? line.length() : end;
+ Timber.tag(tag).i(DEFAULT_LINE + line.substring(start, end));
+ }
+ }
+ }
+
+
+ private static String[] getRequest(Request request) {
+ String log;
+ String header = request.headers().toString();
+ log = METHOD_TAG + request.method() + DOUBLE_SEPARATOR +
+ (isEmpty(header) ? "" : HEADERS_TAG + LINE_SEPARATOR + dotHeaders(header));
+ return log.split(LINE_SEPARATOR);
+ }
+
+ private static String[] getResponse(String header, long tookMs, int code, boolean isSuccessful,
+ List segments, String message) {
+ String log;
+ String segmentString = slashSegments(segments);
+ log = ((!TextUtils.isEmpty(segmentString) ? segmentString + " - " : "") + "is success : "
+ + isSuccessful + " - " + RECEIVED_TAG + tookMs + "ms" + DOUBLE_SEPARATOR + STATUS_CODE_TAG +
+ code + " / " + message + DOUBLE_SEPARATOR + (isEmpty(header) ? "" : HEADERS_TAG + LINE_SEPARATOR +
+ dotHeaders(header)));
+ return log.split(LINE_SEPARATOR);
+ }
+
+ private static String slashSegments(List segments) {
+ StringBuilder segmentString = new StringBuilder();
+ for (String segment : segments) {
+ segmentString.append("/").append(segment);
+ }
+ return segmentString.toString();
+ }
+
+ /**
+ * 对 {@code header} 按规定的格式进行处理
+ *
+ * @param header
+ * @return
+ */
+ private static String dotHeaders(String header) {
+ String[] headers = header.split(LINE_SEPARATOR);
+ StringBuilder builder = new StringBuilder();
+ String tag = "─ ";
+ if (headers.length > 1) {
+ for (int i = 0; i < headers.length; i++) {
+ if (i == 0) {
+ tag = CORNER_UP;
+ } else if (i == headers.length - 1) {
+ tag = CORNER_BOTTOM;
+ } else {
+ tag = CENTER_LINE;
+ }
+ builder.append(tag).append(headers[i]).append("\n");
+ }
+ } else {
+ for (String item : headers) {
+ builder.append(tag).append(item).append("\n");
+ }
+ }
+ return builder.toString();
+ }
+
+
+ private static String getTag(boolean isRequest) {
+ if (isRequest) {
+ return TAG + "-Request";
+ } else {
+ return TAG + "-Response";
+ }
+ }
+
+}
diff --git a/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java b/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java
index eeb5625..0d55e78 100644
--- a/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java
+++ b/arms/src/main/java/com/jess/arms/http/RequestInterceptor.java
@@ -25,6 +25,7 @@
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -79,12 +80,12 @@ public Response intercept(Chain chain) throws IOException {
boolean logRequest = printLevel == Level.ALL || (printLevel != Level.NONE && printLevel == Level.REQUEST);
if (logRequest) {
- boolean hasRequestBody = request.body() != null;
//打印请求信息
- Timber.tag(getTag(request, "Request_Info")).w("Params : 「 %s 」%nConnection : 「 %s 」%nHeaders : %n「 %s 」"
- , hasRequestBody ? parseParams(request.newBuilder().build().body()) : "Null"
- , chain.connection()
- , request.headers());
+ if (request.body() != null && isParseable(request.body().contentType())) {
+ FormatPrinter.printJsonRequest(request, parseParams(request));
+ } else {
+ FormatPrinter.printFileRequest(request);
+ }
}
boolean logResponse = printLevel == Level.ALL || (printLevel != Level.NONE && printLevel == Level.RESPONSE);
@@ -99,15 +100,34 @@ public Response intercept(Chain chain) throws IOException {
}
long t2 = logResponse ? System.nanoTime() : 0;
- if (logResponse) {
- String bodySize = originalResponse.body().contentLength() != -1 ? originalResponse.body().contentLength() + "-byte" : "unknown-length";
- //打印响应时间以及响应头
- Timber.tag(getTag(request, "Response_Info")).w("Received response in [ %d-ms ] , [ %s ]%n%s"
- , TimeUnit.NANOSECONDS.toMillis(t2 - t1), bodySize, originalResponse.headers());
- }
+ ResponseBody responseBody = originalResponse.body();
//打印响应结果
- String bodyString = printResult(request, originalResponse.newBuilder().build(), logResponse);
+ String bodyString = null;
+ if (responseBody != null && isParseable(responseBody.contentType())) {
+ bodyString = printResult(request, originalResponse, logResponse);
+ }
+
+ if (logResponse) {
+ final List segmentList = request.url().encodedPathSegments();
+ final String header = originalResponse.headers().toString();
+ final int code = originalResponse.code();
+ final boolean isSuccessful = originalResponse.isSuccessful();
+ final String message = originalResponse.message();
+ final String url = originalResponse.request().url().toString();
+
+ if (responseBody != null && isParseable(responseBody.contentType())) {
+ FormatPrinter.printJsonResponse(TimeUnit.NANOSECONDS.toMillis(t2 - t1),
+ isSuccessful, code, header,
+ isJson(responseBody.contentType()) ?
+ CharacterHandler.jsonFormat(bodyString) : isXml(responseBody.contentType()) ?
+ CharacterHandler.xmlFormat(bodyString) : bodyString, segmentList, message, url);
+ } else {
+ FormatPrinter.printFileResponse(TimeUnit.NANOSECONDS.toMillis(t2 - t1),
+ isSuccessful, code, header, segmentList, message, url);
+ }
+
+ }
if (mHandler != null)//这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取
return mHandler.onHttpResultResponse(bodyString, chain, originalResponse);
@@ -126,47 +146,29 @@ public Response intercept(Chain chain) throws IOException {
*/
@Nullable
private String printResult(Request request, Response response, boolean logResponse) throws IOException {
- //读取服务器返回的结果
- ResponseBody responseBody = response.body();
- String bodyString = null;
- if (isParseable(responseBody.contentType())) {
- try {
- BufferedSource source = responseBody.source();
- source.request(Long.MAX_VALUE); // Buffer the entire body.
- Buffer buffer = source.buffer();
-
- //获取content的压缩类型
- String encoding = response
- .headers()
- .get("Content-Encoding");
-
- Buffer clone = buffer.clone();
-
-
- //解析response content
- bodyString = parseContent(responseBody, encoding, clone);
- } catch (IOException e) {
- e.printStackTrace();
- }
- if (logResponse) {
- Timber.tag(getTag(request, "Response_Result")).w(isJson(responseBody.contentType()) ?
- CharacterHandler.jsonFormat(bodyString) : isXml(responseBody.contentType()) ?
- CharacterHandler.xmlFormat(bodyString) : bodyString);
- }
-
- } else {
- if (logResponse) {
- Timber.tag(getTag(request, "Response_Result")).w("This result isn't parsed");
- }
+ try {
+ //读取服务器返回的结果
+ ResponseBody responseBody = response.newBuilder().build().body();
+ BufferedSource source = responseBody.source();
+ source.request(Long.MAX_VALUE); // Buffer the entire body.
+ Buffer buffer = source.buffer();
+
+ //获取content的压缩类型
+ String encoding = response
+ .headers()
+ .get("Content-Encoding");
+
+ Buffer clone = buffer.clone();
+
+ //解析response content
+ return parseContent(responseBody, encoding, clone);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return "{\"error\": \"" + e.getMessage() + "\"}";
}
- return bodyString;
}
- private String getTag(Request request, String tag) {
- return String.format(" [%s] 「 %s 」>>> %s", request.method(), request.url().toString(), tag);
- }
-
/**
* 解析服务器响应的内容
@@ -194,27 +196,26 @@ private String parseContent(ResponseBody responseBody, String encoding, Buffer c
/**
* 解析请求服务器的请求参数
*
- * @param body
+ * @param request
* @return
* @throws UnsupportedEncodingException
*/
- public static String parseParams(RequestBody body) throws UnsupportedEncodingException {
- if (isParseable(body.contentType())) {
- try {
- Buffer requestbuffer = new Buffer();
- body.writeTo(requestbuffer);
- Charset charset = Charset.forName("UTF-8");
- MediaType contentType = body.contentType();
- if (contentType != null) {
- charset = contentType.charset(charset);
- }
- return URLDecoder.decode(requestbuffer.readString(charset), convertCharset(charset));
-
- } catch (IOException e) {
- e.printStackTrace();
+ public static String parseParams(Request request) throws UnsupportedEncodingException {
+ try {
+ RequestBody body = request.newBuilder().build().body();
+ if (body == null) return "";
+ Buffer requestbuffer = new Buffer();
+ body.writeTo(requestbuffer);
+ Charset charset = Charset.forName("UTF-8");
+ MediaType contentType = body.contentType();
+ if (contentType != null) {
+ charset = contentType.charset(charset);
}
+ return CharacterHandler.jsonFormat(URLDecoder.decode(requestbuffer.readString(charset), convertCharset(charset)));
+ } catch (IOException e) {
+ e.printStackTrace();
+ return "{\"error\": \"" + e.getMessage() + "\"}";
}
- return "This params isn't parsed";
}
/**
@@ -224,26 +225,39 @@ public static String parseParams(RequestBody body) throws UnsupportedEncodingExc
* @return
*/
public static boolean isParseable(MediaType mediaType) {
- if (mediaType == null) return false;
- return mediaType.toString().toLowerCase().contains("text")
+ return isText(mediaType) || isPlain(mediaType)
|| isJson(mediaType) || isForm(mediaType)
|| isHtml(mediaType) || isXml(mediaType);
}
+ public static boolean isText(MediaType mediaType) {
+ if (mediaType == null || mediaType.type() == null) return false;
+ return mediaType.type().equals("text");
+ }
+
+ public static boolean isPlain(MediaType mediaType) {
+ if (mediaType == null || mediaType.subtype() == null) return false;
+ return mediaType.subtype().toLowerCase().contains("plain");
+ }
+
public static boolean isJson(MediaType mediaType) {
- return mediaType.toString().toLowerCase().contains("json");
+ if (mediaType == null || mediaType.subtype() == null) return false;
+ return mediaType.subtype().toLowerCase().contains("json");
}
public static boolean isXml(MediaType mediaType) {
- return mediaType.toString().toLowerCase().contains("xml");
+ if (mediaType == null || mediaType.subtype() == null) return false;
+ return mediaType.subtype().toLowerCase().contains("xml");
}
public static boolean isHtml(MediaType mediaType) {
- return mediaType.toString().toLowerCase().contains("html");
+ if (mediaType == null || mediaType.subtype() == null) return false;
+ return mediaType.subtype().toLowerCase().contains("html");
}
public static boolean isForm(MediaType mediaType) {
- return mediaType.toString().toLowerCase().contains("x-www-form-urlencoded");
+ if (mediaType == null || mediaType.subtype() == null) return false;
+ return mediaType.subtype().toLowerCase().contains("x-www-form-urlencoded");
}
public static String convertCharset(Charset charset) {
@@ -253,4 +267,5 @@ public static String convertCharset(Charset charset) {
return s;
return s.substring(i + 1, s.length() - 1);
}
+
}