1. 程式人生 > >無異常日誌,就不能排查問題了???

無異常日誌,就不能排查問題了???

## 小聲逼逼 眾所周知,日誌是排查問題的重要手段。關於日誌設計,以及怎麼根據從【使用者報障】環節開始到秒級定位問題這個我們下一期說(絕非套路),這一期,主要講一下,`在沒有異常日誌的情況下`,如何定位問題。沒有日誌當真能排查問題,不會是標題黨吧! ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113649816-927204454.jpg) ## 案例一 從最大的同性交友網站中拉取【dubbo-spring-boot-project】的程式碼。 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113727047-1696471248.png) 然後把demo跑起來。 本場景是由真實案例改編,因為公司程式碼比較複雜也不方便透露,而這個demo在github上大家都能找到,既保證了原汁原味,又能讓大家方便自己體驗排查過程。 好了,我們先設定`owner = "feichao"`,然後看一下控制檯 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113751624-1502748496.png) ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113806609-1691808790.png) 一切正常 那麼,當我設定成`owner = "feichaozhenshuai!"`,再啟動 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113821190-652134581.png) 看似一切都正常,那麼,我們到控制檯一看。 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113840838-656385915.png) 什麼情況,怎麼就沒`owner`了? 這是在哪個環節出問題了?其實肥朝當初在公司遇到這個問題的時候,場景比這個複雜得多。因為公司的業務裡沒有owner的話,在執行時會出現一些其他異常,涉及公司業務這裡就不展開了,我們言歸正傳,為毛我設定成`feichaozhenshuai!`就不行了,那我設定成`肥朝大帥比`電腦會不會爆炸啊??? ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113855233-1124465378.jpg) 常見的`錯誤做法`是,把這個問題截圖往群裡一丟,問“你們有沒有遇到過dubbo裡面,owner設定不生效的問題?” ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113906564-217260706.jpg) 而關注了肥朝公眾號的【真愛粉絲】會這麼問,“dubbo裡面設定owner卻不生效,你們覺得我要從個角度排查問題?”。一看到這麼正確的提問方式,我覺得我不回覆你都不好意思。好了,回到主題,這個時候,沒有一點點錯誤日誌,但是卻設定不成功,我們有哪些排查手段? ### 套路一 直接找set方法,看看是不是程式碼做了判斷,防止在`owner`欄位裡面set`肥朝真帥`這種詞語,`避免把帥這件事走漏風聲!`。這麼一分析似乎挺有道理對吧,那麼,如何快速找到這個set方法呢?如圖 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113922665-918787358.png) ```java public void setOwner(String owner) { checkMultiName("owner", owner); this.owner = owner; } ``` 我們跟進`checkMultiName`程式碼後發現 ```java protected static void checkProperty(String property, String value, int maxlength, Pattern pattern) { if (StringUtils.isEmpty(value)) { return; } if (value.length() > maxlength) { throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" is longer than " + maxlength); } if (pattern != null) { Matcher matcher = pattern.matcher(value); if (!matcher.matches()) { throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" contains illegal " + "character, only digit, letter, '-', '_' or '.' is legal."); } } } ``` 從異常描述就很明顯可以看出,原來`owner`裡面是隻支援`-`和`_`等這類特殊符號,`!`是不支援的,所以設定成不成功,和肥朝帥不帥是沒關係的,和後面的`!`是有關係的。擦,原來是肥朝想多了,給自己加戲了!!! 當然肥朝可以告訴你,在後面的版本,修復了這個bug,日誌會看得到異常了。這個時候你覺得問題就解決了? 我相信此時很多假粉就會關掉文章,或者說下次肥朝發了一些他們不喜歡看的文章(你懂的)後,他們就從此取關,但是肥朝想說,且慢動手!!! ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113938657-448437167.jpg) 你想嘛,萬一你以後又遇到類似的問題呢?而且原始碼層次很深,就不是簡單的搜個`set`方法這麼簡單,這次給你搜到了set方法並解決問題,簡直是`偶然成功`。因此,我才多次強調,要持續關注肥朝,掌握更多套路。這難道是想騙你關注?我這分明是愛你啊! 那麼,萬一以後遇到一些`吞掉異常`,亦或者某些原因導致`日誌沒列印`,我們到底如何排查? ### 套路二 我們知道idea裡面有很多好用的功能,比如肥朝之前的【看原始碼,我為何推薦idea?】中就提到了`條件斷點`,除此之外,還有一個被大家低估的功能,叫做`異常斷點`。 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614113952338-1289989810.png) 肥朝掃了一眼,裡面的單詞都是小學的英語單詞,因此怎麼使用就不做過多解釋。遇到這個問題時,我們可以這樣設定異常斷點。 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114006947-1047219554.png) 執行起來如下: ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114022187-2086248659.png) 這樣,執行起來的時候,就會迅速定位到異常位置。然後一頓分析,應該很容易找出問題。 是不是有點感覺了?那我們再來一個題型練習一下。 ## 案例二 我們先在看之前肥朝粉絲群的提問 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114038887-1424844013.jpg) ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114054080-633343764.jpg) ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114104170-1805001994.jpg) 考慮到部分粉絲不在群裡,我就簡單描述一下這個粉絲的問題,他程式碼有個異常,然後catch打異常日誌,但是日誌卻沒輸出。 當然你還是不理解也沒關係,我根據該粉絲的問題,給你搭建了一個最簡模型的demo,模型雖然簡單,但是問題是同樣的,原汁原味,熟悉的配方,熟悉的味道。git地址如下:【https://gitee.com/HelloToby/springboot-run-exception】 我們執行起來看一下 ```java @Slf4j public class HelloSpringApplicationRunListener implements SpringApplicationRunListener { public HelloSpringApplicationRunListener(SpringApplication application, String[] args) { } @Override public void starting() { } @Override public void environmentPrepared(ConfigurableEnvironment environment) { } @Override public void contextPrepared(ConfigurableApplicationContext context) { throw new RuntimeException("歡迎關注微信公眾號【肥朝】"); } @Override public void contextLoaded(ConfigurableApplicationContext context) { } @Override public void finished(ConfigurableApplicationContext context, Throwable exception) { } } ``` ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114121846-2068588699.png) 你會發現,一執行起來程序就停止,一點日誌都沒。絕大部分假粉絲遇到這個情況,都是菊花一緊,一點頭緒都沒,又去群裡問”你們有沒有遇到過,Springboot一起來程序就沒了,但是沒有日誌的問題?“。正確提問姿勢肥朝已經強調過,這裡不多說。那麼我們用前面學到的排查套路,再來走一波 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114144871-949388949.png) ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114158652-1316520417.png) 我們根據異常棧順藤摸瓜 ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114211694-395008179.png) 我們從程式碼中看出兩個關鍵單詞【reportFailure】、【context.close()】,經過斷點我們發現,確實是會先列印日誌,再關掉容器。但是為啥日誌先執行,再關掉容器,日誌沒輸出,容器就關掉了呢?因為,這個demo中,日誌是全非同步日誌,非同步日誌還沒執行,容器就關了,導致了日誌沒有輸出。 該粉絲遇到的問題是類似的,他是單元測試中,程式碼中的非同步日誌還沒輸出,單元測試執行完程序就停止了。知道了原理解決起來也很簡單,比如最簡單的,跑單元測試的時候末尾先sleep一下等日誌輸出。 在使用Springboot中,其實經常會遇到這種,啟動期間出現異常,但是日誌是非同步的,日誌還沒輸出就容器停止,導致沒有異常日誌。知道了原理之後,要徹底解決這類問題,可以增加一個`SpringApplicationRunListener`。 ```java /** * 負責應用啟動時的異常輸出 */ @Slf4j public class OutstandingExceptionReporter implements SpringApplicationRunListener { public OutstandingExceptionReporter(SpringApplication application, String[] args) { } @Override public void starting() { } @Override public void environmentPrepared(ConfigurableEnvironment environment) { } @Override public void contextPrepared(ConfigurableApplicationContext context) { } @Override public void contextLoaded(ConfigurableApplicationContext context) { } @Override public void finished(ConfigurableApplicationContext context, Throwable exception) { if (exception != null) { log.error("application started failed",exception); try { Thread.sleep(100); } catch (InterruptedException e) { log.error("application started failed", e); } } } } ``` 再囉嗦一句,其實日誌輸出不了,除了這個非同步日誌的案例外,還有很多情況的,比如日誌衝突之類的,排查套路還很多,因此,建議持續關注,每一個套路,都想和你分享! ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114235928-76992904.png) ![](https://img2020.cnblogs.com/blog/840677/202006/840677-20200614114316826-4425330