我對於Error Log的看法

我對於error log的看法,以及在golang中如何使用error log

先講我的結論:我覺得log要儘量少用,頂多只能在最上層進入點使用。

那這樣要怎麼知道發生錯誤的時候,是經由什麼途徑發生的?

那就是在發生錯誤的時候,在錯誤再加上一些訊息,然後再往上傳, 讓最上層收到的error擁有一整串發生錯誤的錯誤訊息。 所以只要在最上層log一次error,就能在error當中看到這個錯誤從上到下的呼叫關係了。 而這好像也是golang官方所建議的寫法

而要能最大化可閱讀性,我個人的建議是: 在發生錯誤是,再wrap上在用什麼參數調用什麼工具發生錯誤了:錯誤訊息

當然這樣做還要配合使用, 遇到錯誤時,一定要往上層傳遞,除非這個error已經有在這層處理完了 這樣才不會省略到某些錯誤,而在發生錯誤的時候,沒有log可以看。

這癢做有個例外:像讀檔讀到盡頭,會收到io.EOF的這種情況, 這個錯誤本來就是讀檔的結束標誌,所以只要進入後續的對應的處理邏輯即可,不用回傳。

實際上的做法大概會是長這樣

//AccountServie.ChangeName()
account, err := s.accountRepo.GetAccount(ctx, id)
if err != nil {
  return fmt.Errorf("cannot get account<%s>: %w", id, err)
}
account.Name = newName
err := s.accountRepo.UpdateAccount(ctx, account)
if err != nil {
  return fmt.Errorf("cannot update account<%s>: %w", id, err)
}
return nil
//handler
err := h.accountService.ChangeName(ctx, id, newName)
if err != nil {
  log.Error("cannot change name of account<%d> to %s: %v",id, newName, err)
}

這樣寫,log出來的Error之後有一行,並且內容大概會長得像是下面這樣

cannot change name of account to Jacky: cannot get account: record not found

這樣就能知道是handler在做change name的時候發生錯誤, 然後在裡面則是在取得account物件的時候出錯了,而底下的錯誤則是根本沒有這個筆資料。

而且還能知道

  • handler調用accountService時使用的id, newName
  • accountService呼叫GetAccount時使用的id是什麼。

這樣做唯一的一個缺點就是,錯誤訊息可能會比較長。

但只要都有照著這個規則來回傳錯誤訊息,就能讓一次的呼叫, 只有一組錯誤訊息,從而簡化閱讀錯誤訊息的困難了。

我現在反倒覺得那種每層加log.Debug、log.Error的做法閱讀起來很困難,因為

  • 訊息會到處都是
  • 訊息很難知道上下文(我知道可以加sessionID、requestID之類的,但這樣做,我覺得還是比較難閱讀)
  • 訊息有時和其它訊息是重複的(每層都log一次,所以會有很多錯誤訊息長得很像之類的)。
comments powered by Disqus