小聲逼逼
衆所周知,日誌是排查問題的重要手段。關於日誌設計,以及怎麼根據從【用戶報障】環節開始到秒級定位問題這個我們下一期說(絕非套路),這一期,主要講一下,在沒有異常日誌的情況下
,如何定位問題。沒有日誌當真能排查問題,不會是標題黨吧!
案例一
從最大的同性交友網站中拉取【dubbo-spring-boot-project】的代碼。
然後把demo跑起來。
本場景是由真實案例改編,因爲公司代碼比較複雜也不方便透露,而這個demo在github上大家都能找到,既保證了原汁原味,又能讓大家方便自己體驗排查過程。
好了,我們先設置owner = "feichao"
,然後看一下控制檯
一切正常
那麼,當我設置成owner = "feichaozhenshuai!"
,再啓動
看似一切都正常,那麼,我們到控制檯一看。
什麼情況,怎麼就沒owner
了?
這是在哪個環節出問題了?其實肥朝當初在公司遇到這個問題的時候,場景比這個複雜得多。因爲公司的業務裏沒有owner的話,在運行時會出現一些其他異常,涉及公司業務這裏就不展開了,我們言歸正傳,爲毛我設置成feichaozhenshuai!
就不行了,那我設置成肥朝大帥比
電腦會不會爆炸啊???
常見的錯誤做法
是,把這個問題截圖往羣裏一丟,問“你們有沒有遇到過dubbo裏面,owner設置不生效的問題?”
而關注了肥朝公衆號的【真愛粉絲】會這麼問,“dubbo裏面設置owner卻不生效,你們覺得我要從個角度排查問題?”。一看到這麼正確的提問方式,我覺得我不回覆你都不好意思。好了,回到主題,這個時候,沒有一點點錯誤日誌,但是卻設置不成功,我們有哪些排查手段?
套路一
直接找set方法,看看是不是代碼做了判斷,防止在owner
字段裏面set類似肥朝真帥
這種詞語,避免把帥這件事走漏風聲!
。這麼一分析似乎挺有道理對吧,那麼,如何快速找到這個set方法呢?如圖
public void setOwner(String owner) {
checkMultiName("owner", owner);
this.owner = owner;
}
我們跟進checkMultiName
代碼後發現
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,日誌會看得到異常了。這個時候你覺得問題就解決了?
我相信此時很多假粉就會關掉文章,或者說下次肥朝發了一些他們不喜歡看的文章(你懂的)後,他們就從此取關,但是肥朝想說,且慢動手!!!
你想嘛,萬一你以後又遇到類似的問題呢?而且源碼層次很深,就不是簡單的搜個set
方法這麼簡單,這次給你搜到了set方法並解決問題,簡直是偶然成功
。因此,我纔多次強調,要持續關注肥朝,掌握更多套路。這難道是想騙你關注?我這分明是愛你啊!
那麼,萬一以後遇到一些吞掉異常
,亦或者某些原因導致日誌沒打印
,我們到底如何排查?
套路二
我們知道idea裏面有很多好用的功能,比如肥朝之前的【看源碼,我爲什麼推薦IDEA?】中就提到了條件斷點
,除此之外,還有一個被大家低估的功能,叫做異常斷點
。
肥朝掃了一眼,裏面的單詞都是小學的英語單詞,因此怎麼使用就不做過多解釋。遇到這個問題時,我們可以這樣設置異常斷點。
運行起來如下:
這樣,運行起來的時候,就會迅速定位到異常位置。然後一頓分析,應該很容易找出問題。
是不是有點感覺了?那我們再來一個題型練習一下。
案例二
我們先在看之前肥朝粉絲羣的提問
考慮到部分粉絲不在羣裏,我就簡單描述一下這個粉絲的問題,他代碼有個異常,然後catch打異常日誌,但是日誌卻沒輸出。
當然你還是不理解也沒關係,我根據該粉絲的問題,給你搭建了一個最簡模型的demo,模型雖然簡單,但是問題是同樣的,原汁原味,熟悉的配方,熟悉的味道。git地址如下:【https://gitee.com/HelloToby/springboot-run-exception】我們運行起來看一下
@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) {
}
}
你會發現,一運行起來進程就停止,一點日誌都沒。絕大部分假粉絲遇到這個情況,都是菊花一緊,一點頭緒都沒,又去羣裏問”你們有沒有遇到過,Springboot一起來進程就沒了,但是沒有日誌的問題?“。正確提問姿勢肥朝已經強調過,這裏不多說。那麼我們用前面學到的排查套路,再來走一波
我們根據異常棧順藤摸瓜
我們從代碼中看出兩個關鍵單詞【reportFailure】、【context.close()】,經過斷點我們發現,確實是會先打印日誌,再關掉容器。但是爲啥日誌先執行,再關掉容器,日誌沒輸出,容器就關掉了呢?因爲,這個demo中,日誌是全異步日誌,異步日誌還沒執行,容器就關了,導致了日誌沒有輸出。
該粉絲遇到的問題是類似的,他是單元測試中,代碼中的異步日誌還沒輸出,單元測試執行完進程就停止了。知道了原理解決起來也很簡單,比如最簡單的,跑單元測試的時候末尾先sleep一下等日誌輸出。
在使用Springboot中,其實經常會遇到這種,啓動期間出現異常,但是日誌是異步的,日誌還沒輸出就容器停止,導致沒有異常日誌。知道了原理之後,要徹底解決這類問題,可以增加一個SpringApplicationRunListener
。
/**
* 負責應用啓動時的異常輸出
*/
@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);
}
}
}
}
再囉嗦一句,其實日誌輸出不了,除了這個異步日誌的案例外,還有很多情況的,比如日誌衝突之類的,排查套路還很多,因此,建議持續關注,每一個套路,都想和你分享!
什麼是編程思想?
肥朝始終覺得,要想比別人更優秀,除了比別人更努力這個必要因素外,思維方式,也是我們必要關注的一個重點。比如在案例二中,很多同學知道了bug之後,就認爲自己學到東西了,其實這個想法既正確,也不正確。
正確的地方在於,你知道了這個bug,後面遇到相同的問題,你會猜一下是不是同樣的原因。
不正確的地方在於,你只知道了這個bug出現的某個場景,但是當我們遇到這個問題,應對的排查套路有哪些你並不知道。也就是說,如果這個問題過後,你排查問題的套路並沒有增加,亦或者你沒有能從這個問題上,發散出自己的想法,繼續壓榨出更多的價值,本質上,你的編程能力,其實並沒有提升的。
然而,你一旦在公司時間長了,也就是我們常說的老油條,對公司的某些坑熟悉,新人遇到問題時,就容易猜對可能是某個坑。但是其實你的套路來來去去就那幾個,本質上你的編程能力並沒有提升,卻讓你產生了自己越來越牛逼,這下必須要加薪的錯覺。
一個公司總是有線上報障是有問題的,但是一直不出問題也有問題的。當然很多時候,排查的機會或許輪不到你。這個時候,就會有常見的幾種做法。
1.公司確實項目太簡單,基本沒有什麼拿得出手的bug,都是一些低級的漏掉配置的bug。
2.大佬們在排查,反正不是我的問題,那我就看羣吹吹水,下班美滋滋。
3.大佬們在排查,等他們有結論了,我就過去問一句是啥問題,然後暗自記下來,下次面試的時候就說是自己排查的,吹一波,美滋滋。
4.大佬們在排查,得知原因後,深入思考,大佬們爲啥會想到是這個原因,他們是怎麼排查的?用了哪些排查工具?排查技巧?然後暗自總結一波,並把自己代入場景,腦補一下自己來排查問題,並把這個bug壓榨出更多價值!(怎麼壓榨出更多價值,可以查看肥朝之前的源碼實戰文章,每一篇都有一個環節專門講拓展思考的)
你的思維方式,你的行動,往往就決定你成爲什麼樣的人。肥朝也始終相信,時間在哪,行動在哪,成就就在哪。一起共勉。