@鄭昀匯總 建立日期:2012/10
#意識
ASAP (As Soon As Possible)原則
當線上出現詭異問題,當你意識到靠現有的日誌無法定位問題時,當現象難以在你的開發環境重現時,請不要執著於枯坐肉眼看代碼,因為:一)不一定是你代碼邏輯問題,可能是髒資料造成的,是老業務資料造成的,是分布式環境造成的,是其他子系統造成的;二)線上業務處於不穩定中,條件不允許問題定位無限期。此時,
請立即在問題相關的調用鏈條上,一次性:
- 在函數的入口和出口列印日誌,同時列印輸入、輸出參數
- catch(){……}裡列印stacktrace,同時列印try塊中關鍵變數的值(避免你發現某個異常是問題第一原因,卻不知道是什麼變數傳入導致的)
- 與其他模組互動的介面入口處列印輸入參數,
即,
解決線上問題歸根結底要靠log、a lot of log output!在logging的力度上切勿猶猶豫豫,我們的工程師習慣於吝嗇地找兩個函數列印日誌、打包部署一把、沒看出來、再找幾個函數列印、再部署、等著現象重現再觀察、……,一來二去時間流逝,閑庭信步,從客服知道的小事故變成了全國皆知的大事故。所以,再強調一遍:
在你的調用鏈條上,逐層調用的函數入口和出口都列印詳細日誌,不怕多隻怕少,然後部署,等待現象重現,畢其功於一役!
我們要記錄什嗎?1)完成某項操作所需的時間
通過它可以跟蹤為什麼系統響應變慢或者太快
- 處理完一個incoming request所耗費的時間,精確到毫秒
- 執行資料庫查詢的時間
- 從磁碟或者儲存介質擷取資料的時間
- 等等
2)異常和堆疊追蹤 3)Sessions知道一個問題是由誰引起的非常重要,因此在日誌中使用工作階段識別項就變得必不可少。它可以簡單到是一個 IP 位址或者是一個更複雜的 UUID,只要能區分不同的要求者就足夠。 4)版本號碼
#工具
推薦的Java Logging架構1)log4j:我們的配置是,log4j.appender.CONSOLE.layout.ConversionPattern=[%-d{yyyy-MM-dd HH\:mm\:ss.SSS}] [%p] [%c] [%m]%n;%p是日誌優先順序,%c是類目名,%m是輸出資訊,%n是斷行符號分行符號。2)logback:log4j建立人Ceki Gülcü後續推出了SLF4J+logback。SLF4J(Simple Logging Facade for Java)作為commons-logging的替代,為各種logging APIs提供了一個簡單的統一介面,使得終端使用者能夠在部署的時候配置所希望的logging APIs的實現。logback勝在效能,據稱“某些關鍵操作,比如判定是否記錄一條日誌語句的操作,其效能得到了顯著的提高。這個操作在logback中需要3納秒,而在 log4j 中則需要30納 秒。 logback 建立記錄器(logger)的速度也更快:13毫秒,而在 log4j 中需要23毫秒。更重要的是,它擷取已存在的記錄器只需94納秒, 而 log4j 需要2234納秒,時間減少到了1/23。跟java.util.logging(JUL)相比效能提高也是顯著的”。
#配置
不要隨便從網上找一個log4j的設定檔,請確認你理解每一個配置項我們既然輸出日誌,自然期望在面對“
這個問題是否從過去幾天開始出現?”這樣的疑問時,不至於發現你的rollingPolicy錯誤設定導致只能看到最近幾小時的日誌,或者日誌發生時間沒有精確到毫秒。後面會張貼主站生產環境裡的log4j配置。
#理念
可用grep抽取的日誌:獨立的行!我們總是希望能用grep處理記錄檔。這意味著:
一個日誌條目永遠不應該跨多行,除非你是堆棧列印。我們會用grep問日誌什麼問題呢?如:
- 用手機號13910******下單的顧客最近三天內都來自於哪些IP?
- 瀏覽地址是****?from=kfapi的顧客,但referral卻是搜尋引擎網域名稱,最近三天有多少次?
- 最近一周內,訂單中心執行的所有事務,耗時最長的一次是多長時間?
- ××××的介面是否真的於18:00發送了一個請求,我們收到的參數是什嗎?
確保你的日誌能回答這樣的問題。
不同關注領域寫不同的記錄檔當訪問和調用極其頻繁,有時候你會發現把你的工程裡什麼資訊都列印到一個記錄檔裡,會讓你看得頭昏腦脹。最簡單的示範就是Apache的訪問日誌和錯誤記錄檔是分開的。同樣,你也可以把更加安靜的事件(偶爾出現)與更加喧鬧的事件分開儲存。如,對外的開放平台可以列印三種記錄檔:connection log(建立連結和關閉連結,附帶接入參數),message log(內部調用鏈),stacktrace log(異常的堆棧列印)。
#具體實現
至少精確到毫秒日誌必須包含時間戳記,精確到至少毫秒級。如果只是記錄到秒級,我們曾明知代碼因缺乏並發控制而產生BUG,卻只能鬱悶地看著精確到秒級的日誌。對Java來說,最好配置為:yyyy-MM-dd/HH:mm:ss.SSS。
請儘可能列印明確的會話標識日誌條目裡列印一個會話標識(A certain session identifier),當有許多並發請求打過來時,你就能基於此欄位過濾 client 了。比如,我們司南日誌會補充列印一個瀏覽器 cookies 裡種下的 UUID 。
log4j的isDebugEnabled判斷如果列印資訊是常量字串或簡單字串拼接,那麼不需要if ( log.isDebugEnabled() )。如果你拼裝的動作比較耗資源,請用if ( log.isDebugEnabled() )。
如有可能,請將效能資料標準化輸出這樣更方便grep或hadoop做效能資料抽取和挖掘,從而能很輕鬆地轉換為圖形監控。比如,訂單中心的效能資料格式為:
樹枝標誌 當前節點起始時間 [當前節點期間, 當前節點自身消耗時間, 在父節點中所佔的時間比例]
哪些位置需要部署效能檢測點 (1)訪問資料庫的dao層;(2)訪問外部資源的ext層;(3)訪問mq的方法;(4)等等,一切不在你自己負責的工程掌握的部分(外部),或一切你認為自己工程的效能危險點,都需要加入效能監控日誌。
#Sample
一個好的開機記錄列印了應用的版本號碼,用戶端的會話標識,關鍵步驟的執行時間長度。
一個好的堆疊追蹤日誌 本文首發於旁觀者-鄭昀的55最佳實務系列,連結:http://www.cnblogs.com/zhengyun_ustc/archive/2012/12/15/logging_bp.html
參考資源:1,紅薯,Logging 日誌記錄最佳實務,英文原文2,Julius Davies:Log4j Best Practices,譯文在此3,十個轉移到logback的理由[PPT]1)55最佳實務系列:MongoDB最佳實務 (2012-12-15 15:48)
2)55最佳實務系列:Logging最佳實務 (2012-12-15 16:43)
3)
贈圖1枚: