Day24-Retrofit&Logger

準(zhǔn)確的說是OkHttp + Logger 打印清晰的日志

目的:

  1. 通過 retrofit/okhttp 的支持, 先打印出混著的網(wǎng)絡(luò)請求log


  2. 然后通過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,更新了部分方法投蝉,其他地方相同

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末养葵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瘩缆,更是在濱河造成了極大的恐慌关拒,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庸娱,死亡現(xiàn)場離奇詭異着绊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)涌韩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門羡鸥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冷溶,“玉大人搀崭,你說我怎么就攤上這事『顺ィ” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵恒界,是天一觀的道長喂柒。 經(jīng)常有香客問我,道長棚放,這世上最難降的妖魔是什么枚粘? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮飘蚯,結(jié)果婚禮上馍迄,老公的妹妹穿的比我還像新娘。我一直安慰自己局骤,他們只是感情好攀圈,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峦甩,像睡著了一般赘来。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凯傲,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天犬辰,我揣著相機(jī)與錄音,去河邊找鬼冰单。 笑死幌缝,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诫欠。 我是一名探鬼主播狮腿,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呕诉!你這毒婦竟也來了缘厢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤甩挫,失蹤者是張志新(化名)和其女友劉穎贴硫,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伊者,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡英遭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了亦渗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挖诸。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖法精,靈堂內(nèi)的尸體忽然破棺而出多律,到底是詐尸還是另有隱情痴突,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布狼荞,位于F島的核電站辽装,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏相味。R本人自食惡果不足惜拾积,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丰涉。 院中可真熱鬧拓巧,春花似錦、人聲如沸一死。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摘符。三九已至贤斜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逛裤,已是汗流浹背瘩绒。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留带族,地道東北人锁荔。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像蝙砌,于是被迫代替她去往敵國和親阳堕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容