準(zhǔn)確的說是OkHttp + Logger 打印清晰的日志
目的:
-
通過 retrofit/okhttp 的支持, 先打印出混著的網(wǎng)絡(luò)請求log
-
然后通過logger打印成下面這樣
一. 先打印出log
0. 借著 okhttp 提供了 interceptor 控制 Logger
過去跟服務(wù)器的接口連調(diào)我是打印出 token, 然后在 postman 創(chuàng)建鏈接拼入?yún)?shù)(怕玩意來個空就崩肘习。际乘。), 后來大家配合的好點兒了, 不怕崩就直接在log里看, 但是看到超長的json...還是得復(fù)制出來, 然后用Notepad++之類的格式化來看, 既然okhttp提供了interceptor, 那就來試試看吧
1. 添加square的logging-interceptor
compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'
2. 擴(kuò)展自己的log工具
public class HttpLogger implements HttpLoggingInterceptor.Logger {
@Override
public void log(String message) {
Log.d("HttpLogInfo", message);
}
}
3. 擴(kuò)展log工具集成進(jìn)okhttp
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger());
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addNetworkInterceptor(logInterceptor);//retrofit 的 builder設(shè)置okhttp內(nèi)核
3.1 為啥是 addNetworkInterceptor 而不是 addInterceptor
在給okhttp擴(kuò)展的時候 應(yīng)該是 addNetworkInterceptor 而不是 addInterceptor, 因為有時候可能會通過cookieJar在header里面添加一些持久化的cookie或session信息. 這樣請求頭就打印不出信息了
來看下Realcall.java的源碼
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
okhttp在做請求的時候會先構(gòu)造攔截鏈, 然后將所有的攔截器都放入一個ArrayList, 添加順序和代碼的一樣:
client.interceptors());
retryAndFollowUpInterceptor);
BridgeInterceptor(client.cookieJar()));
CacheInterceptor(client.internalCache()));
ConnectInterceptor(client));
client.networkInterceptors());
CallServerInterceptor(forWebSocket));
當(dāng)使用 addInterceptor 添加攔截器時, 會直接通過 client.networkInterceptors() 添加, 然后按照順序攔截的時候, 會是 HttpLoggingInterceptor 先執(zhí)行, 然后打印出日志, 然后才會執(zhí)行 CookieJar 包裝的攔截器 BridgeInterceptor, 這樣導(dǎo)致我們添加到 header 中的 cookie 等信息不會打印出來
二. 接下來開始格式化 log
4. 添加 Logger
compile 'com.orhanobut:logger:2.1.1'
5. 用 LogUtil 封裝 Logger
public class LogUtil {
/**
* 初始化log工具,在app入口處調(diào)用
*
*/
public static void init() {
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.showThreadInfo(false)
.methodOffset(2)//隱藏內(nèi)部方法
.tag("My custom tag")
.build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy){
@Override
public boolean isLoggable(int priority, String tag) {
return BuildConfig.DEBUG;//是否顯示logger
}
});
}
public static void d(String message) {
Logger.d(message);
}
public static void i(String message) {
Logger.i(message);
}
public static void w(String message, Throwable e) {
String info = e != null ? e.toString() : "null";
Logger.w(message + ":" + info);
}
public static void e(String message, Throwable e) {
Logger.e(e, message);
}
public static void json(String json) {
Logger.json(json);
}
}
6. 初始化 LogUtil
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
LogUtil.init();
}
}
7.格式化json(添加空格, \xx轉(zhuǎn)義成漢字)
public class JsonUtil {
/**
* 格式化json字符串
*
* @param jsonStr 需要格式化的json串
* @return 格式化后的json串
*/
public static String formatJson(String jsonStr) {
if (null == jsonStr || "".equals(jsonStr)) return "";
StringBuilder sb = new StringBuilder();
char last = '\0';
char current = '\0';
int indent = 0;
for (int i = 0; i < jsonStr.length(); i++) {
last = current;
current = jsonStr.charAt(i);
//遇到{ [換行漂佩,且下一行縮進(jìn)
switch (current) {
case '{':
case '[':
sb.append(current);
sb.append('\n');
indent++;
addIndentBlank(sb, indent);
break;
//遇到} ]換行脖含,當(dāng)前行縮進(jìn)
case '}':
case ']':
sb.append('\n');
indent--;
addIndentBlank(sb, indent);
sb.append(current);
break;
//遇到,換行
case ',':
sb.append(current);
if (last != '\\') {
sb.append('\n');
addIndentBlank(sb, indent);
}
break;
default:
sb.append(current);
}
}
return sb.toString();
}
/**
* 添加space
*
* @param sb
* @param indent
*/
private static void addIndentBlank(StringBuilder sb, int indent) {
for (int i = 0; i < indent; i++) {
sb.append('\t');
}
}
/**
* http 請求數(shù)據(jù)返回 json 中中文字符為 unicode 編碼轉(zhuǎn)漢字轉(zhuǎn)碼
*
* @param theString
* @return 轉(zhuǎn)化后的結(jié)果.
*/
public static String decodeUnicode(String theString) {
char aChar;
int len = theString.length();
StringBuffer outBuffer = new StringBuffer(len);
for (int x = 0; x < len; ) {
aChar = theString.charAt(x++);
if (aChar == '\\') {
aChar = theString.charAt(x++);
if (aChar == 'u') {
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = theString.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException(
"Malformed \\uxxxx encoding.");
}
}
outBuffer.append((char) value);
} else {
if (aChar == 't')
aChar = '\t';
else if (aChar == 'r')
aChar = '\r';
else if (aChar == 'n')
aChar = '\n';
else if (aChar == 'f')
aChar = '\f';
outBuffer.append(aChar);
}
} else
outBuffer.append(aChar);
}
return outBuffer.toString();
}
}
8. 搞定
參考
掘金 | 利用 logger 打印完整的 okhttp 網(wǎng)絡(luò)請求和響應(yīng)日志 只是將Logger1.5升到了2.1,更新了部分方法投蝉,其他地方相同