准确的说是OkHttp + Logger 打印清晰的日志
目的:
-
通过 retrofit/okhttp 的支持, 先打印出混着的网络请求log
-
然后通过logger打印成下面这样
一. 先打印出log
0. 借着 okhttp 提供了 interceptor 控制 Logger
过去跟服务器的接口连调我是打印出 token, 然后在 postman 创建链接拼入参数(怕玩意来个空就崩。。), 后来大家配合的好点儿了, 不怕崩就直接在log里看, 但是看到超长的json...还是得复制出来, 然后用Notepad++之类的格式化来看, 既然okhttp提供了interceptor, 那就来试试看吧
1. 添加square的logging-interceptor
compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'
2. 扩展自己的log工具
public class HttpLogger implements HttpLoggingInterceptor.Logger {
@Override
public void log(String message) {
Log.d("HttpLogInfo", message);
}
}
3. 扩展log工具集成进okhttp
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger());
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addNetworkInterceptor(logInterceptor);//retrofit 的 builder设置okhttp内核
3.1 为啥是 addNetworkInterceptor 而不是 addInterceptor
在给okhttp扩展的时候 应该是 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在做请求的时候会先构造拦截链, 然后将所有的拦截器都放入一个ArrayList, 添加顺序和代码的一样:
client.interceptors());
retryAndFollowUpInterceptor);
BridgeInterceptor(client.cookieJar()));
CacheInterceptor(client.internalCache()));
ConnectInterceptor(client));
client.networkInterceptors());
CallServerInterceptor(forWebSocket));
当使用 addInterceptor 添加拦截器时, 会直接通过 client.networkInterceptors() 添加, 然后按照顺序拦截的时候, 会是 HttpLoggingInterceptor 先执行, 然后打印出日志, 然后才会执行 CookieJar 包装的拦截器 BridgeInterceptor, 这样导致我们添加到 header 中的 cookie 等信息不会打印出来
二. 接下来开始格式化 log
4. 添加 Logger
compile 'com.orhanobut:logger:2.1.1'
5. 用 LogUtil 封装 Logger
public class LogUtil {
/**
* 初始化log工具,在app入口处调用
*
*/
public static void init() {
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.showThreadInfo(false)
.methodOffset(2)//隐藏内部方法
.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转义成汉字)
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);
//遇到{ [换行,且下一行缩进
switch (current) {
case '{':
case '[':
sb.append(current);
sb.append('\n');
indent++;
addIndentBlank(sb, indent);
break;
//遇到} ]换行,当前行缩进
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 请求数据返回 json 中中文字符为 unicode 编码转汉字转码
*
* @param theString
* @return 转化后的结果.
*/
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 网络请求和响应日志 只是将Logger1.5升到了2.1,更新了部分方法,其他地方相同