1. 程式人生 > >記一次線上報錯日誌問題排查

記一次線上報錯日誌問題排查

今天陽光明媚,掐指一算,今天比較適合划水。 於是早上到公司之後先是蹲了廁所,然後就準備翻閱公眾號推文。 看的正嗨,突然釘釘群裡開始響了, 生產日誌群報了一條警告,如下: ![報錯資訊](https://images.cnblogs.com/cnblogs_com/barantt/1955593/o_2104010550409D5C21B8-6CFF-484F-9694-9917BD726206.png) 報錯資訊很明確 ```java UnsupportedOperationException java.lang.UnsupportedOperationException at java.util.AbstractMap.put(AbstractMap.java:209) at com.ifugle.rap.dsb.bot.service.messageBus.chatResult.postProcessor.BizCategoryResultResolveProcessor.concatBizCategoryChatResult(BizCategoryResultResolveProcessor.java:98) ``` 定位到業務程式碼如下 ```java /** * 拼接業務多輪聊天結果 * * @param utterance * @param chatResult */ private void concatBizCategoryChatResult(String utterance, Map chatResult) { Object botAction = bizCategoryChatResult.get(MESSAGE_BOT_ACTION); if (NullUtil.isNotNull(botAction)) { chatResult.put(MESSAGE_BOT_ACTION, botAction);//--->這行程式碼報錯 chatResult.put(DISPLAY_CONTENT, bizCategoryChatResult.get(DISPLAY_CONTENT)); chatResult.put(MESSAGE_BOT_FRAMEWORK, bizCategoryChatResult.get(MESSAGE_BOT_FRAMEWORK)); } } ``` 一個普普通通的map的put操作,怎麼就報錯了呢?繼續往下看。 報錯是在AbstractMap,翻看原始碼 ```java public V put(K key, V value) { throw new UnsupportedOperationException(); } ``` 這個抽象累定義了一個public的put方法,但是裡面是直接丟擲了異常。 分析一下,應該是某個類繼承了AbstractMap這個類,但是又沒有重寫put方法,於是就直接呼叫了父類的put方法導致直接拋異常了。 我又翻看了阿里雲上的日誌發現傳過來的這個map是一個空的。 我的第一反應就是集合工具類Collections裡面的靜態方法emptyMap(),因為我們業務程式碼中有很多地方都用到了這個。 這裡貼一小段程式碼 ```java if (!continueSendToRobot) { chatResult = toManResult.getResult(); if (chatResult == null) { chatResult = EMPTY_CHAT_RESULT;//<---- 看到這個EMPTY_CHAT_RESULT沒 } chatMessageForm.setChatResult(chatResult); return ExitHandle.class; } ``` 這個是個靜態變數,於是我又找到了定義它的地方 ```java // 空的聊天內容 Map EMPTY_CHAT_RESULT = Collections.emptyMap(); ``` 繼續看這個emptyMap() ```java public static final Map emptyMap() { return (Map) EMPTY_MAP; } //又是一個靜態變數 //繼續看 public static final Map EMPTY_MAP = new EmptyMap<>(); //這裡是new了一個EmptyMap物件 //繼續看這個物件,如果這個物件是繼承了AbstractMap恰好它沒有重寫put方法的話,那就證明我的猜想每問題 private static class EmptyMap extends AbstractMap implements Serializable //可以看到這個EmptyMap是Collections的一個靜態內部類,繼承了AbstractMap ``` 再看看這個類的所有方法 ![AbstractMap的所有方法](https://images.cnblogs.com/cnblogs_com/barantt/1955593/o_2104010621323D2E1866-4836-4611-9EEF-EF9247CFBCBD.png) 可以看到該類並沒有重寫put方法! 破案了! 那麼問題來了,怎麼解決呢? 後面我在業務程式碼裡面加了一個判斷邏輯,當這個map是AbstractMap並且是一個空map時,重新給他new一個HashMap。 **錯誤示範請勿參考!** ```java /** * 拼接業務多輪聊天結果 * * @param utterance * @param chatResult */ private void concatBizCategoryChatResult(String utterance, Map chatResult) { // 新加程式碼 if (NullUtil.isNull(chatResult) && chatResult instanceof AbstractMap) { chatResult = new HashMap<>(); } Object botAction = bizCategoryChatResult.get(MESSAGE_BOT_ACTION); if (NullUtil.isNotNull(botAction)) { chatResult.put(MESSAGE_BOT_ACTION, botAction);//--->這行程式碼報錯 chatResult.put(DISPLAY_CONTENT, bizCategoryChatResult.get(DISPLAY_CONTENT)); chatResult.put(MESSAGE_BOT_FRAMEWORK, bizCategoryChatResult.get(MESSAGE_BOT_FRAMEWORK)); } } ``` 至此,問題就解決啦~~ 繼續划水。 --- 更新一下,非常感謝[@mrfangzheng](https://www.cnblogs.com/mrfangzheng/)在評論裡面的指正,我上面這種BUG的修復方案是**錯誤**的,學習了謝謝! 我現在的解決方案就是把這個map返回回去,也是這位老哥給的建議,再次感謝。 修正版如下 ```java /** * 拼接業務多輪聊天結果 * * @param utterance * @param chatResult */ private Map concatBizCategoryChatResult(String utterance, Map chatResult) { // 新加程式碼 if (NullUtil.isNull(chatResult) && chatResult instanceof AbstractMap) { chatResult = new HashMap<>(); } Object botAction = bizCategoryChatResult.get(MESSAGE_BOT_ACTION); if (NullUtil.isNotNull(botAction)) { chatResult.put(MESSAGE_BOT_ACTION, botAction);//--->這行程式碼報錯 chatResult.put(DISPLAY_CONTENT, bizCategoryChatResult.get(DISPLAY_CONTENT)); chatResult.put(MESSAGE_BOT_FRAMEWORK, bizCategoryChatResult.get(MESSAGE_BOT_FRAMEWORK)); } return chatResult; } ```