2011-01-28

10 Tips for Proper Application Logging

資料來源:10 Tips for Proper Application Logging
  • 考慮 log4j 以外的選擇 - SLF4J, Logback
    • 使用 SLF4J 的第一個理由
      log.debug("{} records found in this area, {}", cnt, area);
    • 不用一堆加號來組合出 log 訊息。
    • 如果該 log level 不輸出,可以改善文字接龍的小小效能,也省掉了囉唆的 isDebugEnabled()。
  • logging level 很重要
    • ERROR - 發生不得不處理的錯誤,導致程式無法繼續,像是資料庫連線、NPE等。
    • WARN - 程式可以繼續、但是要特別注意的狀況,例如使用備份資料代替正式資料或者系統以除錯模式啟動等。
    • INFO - 系統完成重要的商業流程或更改重要的狀態,必須讓管理者可以事後追蹤。
    • DEBUG - 開發者專用,稍後再詳述。
    • TRACE - 專門供開發過程紀錄詳細的訊息使用,可以在佈署到正式環境後仍然持續紀錄一段時間,但最終是會關閉的;在一項功能經過開發與測試完成後,是否可以移除該 log 程式,為 TRACE  與 DEBUG 的最主要差異所在。
    • 基本原則是,紀錄所有大大小小的事情,但是要分 level,不然就要有心裡準備從草堆裡找一根針 。
    • 除非可以證明真的造成效能瓶頸,否則沒必要使用 is*Enabled(),更何況還有 SLF4J 的 placeholder 可以用。
  • log 的訊息很重要
    • 不要在產生 log 訊息時發生 error,尤其是 NPE。
    • 不要 log 整個 collection 物件,因為可能出錯的地方太多了,如記憶體爆掉、N+1 Select、Lazy Initialization、log 訊息太多等,建議只要 log 物件的 id 或甚至 collection size 就可以了。
    • 如果可以確保 collection 沒問題,這時候 is*Enabled() 就派上用場了。
      if (log.isDebugEnabled()) {
      log.debug(...output the whole collection...);
      } else {
      log.info(...output collection size only...);
      }
    • 一定要實做 toString(),建議用 ToStringBuilder,但是不要用 reflective 那個 method。
  • 避免副作用
    • 最常見的副作用就是 log 整個 Hibernate collection,導致 Lazy initialization exception 發生,好發於 log level 加大時。
    • 另一個常見的副作用是效能,例如 log 太多、toString() 沒實做好、字串連接。
    • 還有 NPE 也是一種副作用。
  • log 訊息要淺顯易懂
    • 每段 log 訊息都要包含資料與敘述。
    • 不要 log 只有自己看得懂得
    • 不可 log 像密碼之類的重要資訊。
  • log 訊息的 pattern
    • 不要紀錄重複的資訊,如果使用每小時一個 log 檔的話,那就不用在 log 裡紀錄時間資訊。
    • 如果是單一執行緒的系統,就不用紀錄 thread name。
    • 必要的 log 資訊:時間(準確到毫秒)、log level、thread name、logger name(不用帶 package path)。
    • 一定不要有的 log 資訊:file name、class name、line  number,尤其 class name、method name 和 line number 相當耗效能。
  • input and output
    • log 第一準則,傳入參數要 log,回傳值也要 log,搭配美美的文字敘述,讓 log 看起來像小說一樣引人入勝,就萬無一失了。
    • 頭尾都顧,很容易就知道程式死哪去了,連 debug 都省了,尤其是不能 debug 的正式環境。
    • DEBUG 或 TRACE 是最適合做守衛的。
    • 如果有些 method 或 class 呼叫太頻繁,也許可以考慮不用請守衛,但是多半時候,「log」到用時方恨少。
  • 外部系統
    • 和前一點一樣,進出都要做紀錄。
  • Exception
    • 不要 log exception,讓 framework 或 container 來處理。
    • 不要 log exception 後,回傳預設值,例如 0、null 或空字串。
    • 不要 log exception 後,用另一個 exception 包起來再丟出去,這樣會讓同一個 excpetion stack tree 出現兩次,log 或者包起來丟出去只能二選一。
    • 如何用 SLF4J 正確的 log excption:
      • 為了確保 stack tree 可以完整 log 下來,一定要將 exception 物件傳給 log。
        log.error("Error: " + e);
        log.error(e.getMessage());
        log.error(e.toString());
        log.error("Error: {}", e);
      • 這四種方式都只會吐出一行錯誤訊息,不會有 stack tree,第四種方式是因為預設呼叫 e.toString()。
      • 正確的方式:
        log.error("Error: ...what happens...", e);
      • 多此一舉的方式,因為 e 物件已經有所有需要的資訊了,第一個字串參數只要補充說明即可。
        log.error("Error: " + e.getMessage(), e);
  • log 訊息除了給人看以外
    • 最好電腦也可以看得懂,尤其當每小時產生幾百 MB 的 log 時。
    • 適當地加上一些格式,方便使用 Regular Expression 等工具來快速篩選出想要的資訊 。

    沒有留言:

    張貼留言