Google Prettify

2015-03-11

好的日誌配置讓你上天堂,不好日誌配置會讓你抓狂:我如何使用日誌配置

在開發軟體的過程中,時常需要在畫面上檢視一個變數值。 最值接的做法,就是使用System.out.print(value); 。 使用print的方式,輸出的標的,預設是標準輸出介面,也就是螢幕。 可如果你想知道正在上線中的產品, 程式碼中的變數值,這樣的做法就很不恰當了。 因為你不會隨時隨地的待在主機前面,盯著螢幕,看著畫面顯示變數值;更甚至,有時因為資料量太多,畫面就這樣一閃而過,根本看不到。 這時如果有一個機制,可以將標準輸出存到檔案去,就很理想了。

正文開始

講了那麼多,除了是練練打字、充充篇幅、騙騙稿費外,簡單一句話,就是「介紹logger的機制」。

我在大多數的案子中,使用的是Log4J這個framework~ ,或者說是機制。 不過近年改用Slf4j。

Log4j是一個老牌的logger 機制,1999年就出來了,正式發行第1版則是到了2001年的事。 不過一直到2006、2007 就沒什麼變動。 2012年才又出了第2版。 這也是我為什麼好好用的log4j,會再去接觸slf4j的原因。 因為這幾年(2007~2012)幾乎是沒什麼更新。

為什麼要使用logger

一開始在學習程式設計時,不知道 logger 與System.out.pring的分別,當實際有了專案經驗後,才發覺這真是無與倫比的重要。

  • 第一個理由,其實在前段的廢文有提到(那這樣是不是可以就不算廢文?XD):必需要將訊息保存下來。 螢幕會一閃而過,留在檔案中才得以保存下來。
  • 將不同的訊息在不同的地方呈現。 一般來說,串流預設的輸出是螢幕。不過如果所有的訊息都一起在螢幕上呈現的話,實在是太雜亂無章,不容易追蹤。 而且當遇到執行方式沒有畫面可以輸出的時候,系統就不知道要輸出到那裡去了。 另外在J2EE環境下,Container(即一般所謂的Tomcat或是JBoss等AP Server) 通常會將該是螢幕輸出的串流,存到自己的log檔。 如果container中有多個AP,則所有AP的log都會顯示在同一份log檔中。 在這種情況下,想要僅僅檢視自己負責的程式所產生的log資訊並不容易,而且還有資安的疑慮。
  • 如果在開發時其大量使用System.out.pring顯示變數資訊或其他訊息,要麻是在開發完成前夕,搜尋所有的System.out.print,然後刪除,要麻就是直接放在那裡, 任由訊息輸出,降低系統效率。這時如果在砍掉System.out.print的時候,萬一不小心砍錯,導致系統錯誤,就得不嘗失了。

基本上,如果使用log framework,以上的問題,都可以提供良好的解決方案。

Logger 組成原素

所謂的組成原素決對不是風、火、地、水,或是金、木、水、火、土 XD。

Logger

基本上 就是logger的實體。通常來說,如果你不是使用System.out.print的話,一般都會是用

Logger this islogger = Logger.getLogger(getClass().getName());
thisislogger.info("This is message~");

變數thisislogger 就是一個logger。 通常logger有自己名稱(指得不是變數名稱而是getClass().getName())。 Log4j的建議與一般慣例是使用package的名稱。 這可以讓logger有良好的繼承機制。當然,如果你想直接使用像"AP"這樣的名稱也是可以,不過這樣在管理上可能就不是那麼容易。

Appender

簡單來說,就是log訊息的輸出地。(其實我也不知道它為什麼要叫「appender」;叫「output」這樣的名字,不是比較直覺…)。以log4j來說,他至少提供了可以將log訊息導到consol上或是實體檔案上的功能。 Slf4j這個log framework 甚至提供可以將log訊息存到DB的功能。 (log4j不確定是否也提供相同的功能)。

Level

指的是logger的輸出等級或是訊息的顯示等級。 訊息輸出的等級必需高於設定檔指定的顯示等級,訊息才會被記錄下來。例如:

如果設定檔中(以log4j為例)是這樣設定:

log4j.appender.foo=info

這是說明了「我有個logger叫foo。 他的輸出等級是info」。 而在Java中這樣撰寫:

foologger.info("this is info message");
foologger.debug("this is detail message");

這說明了我記錄了2個訊息,一個是使用info等級來記錄,一個是debug等級來記錄。 在這樣的配置設定下,只有info等級的訊息會被紀錄下來。

Layout

簡單來說,就是訊息的格式。

舉個範例(還是以log4j為例):

log4j.appender.foo.layout.ConversionPattern=[FOO]%d{HH:mm:ss S} %5p %c{1}:%L - %m%n

%後面接的都是參數。 當然,參數的意義,請參見各log framework的文件。

我的經驗

一個系統裡,可以設置多個logger,讓每個logger,因應不同的情況輸出到不同的地方,或是多個地方。 我舉一些例子。

Framework 的logger,通常為最低,而且輸出是畫面

因為log4j是很老牌的log framework,許多framework 也都會使用。 像Spring或是第三方frameowrk裡的logger我通常會使用最低等級的level ,並且輸出到console appender上。 因為我會需要去看spring裡的變數時,通常是在開發階段。 這樣的階段,也通常會有console可供使用,而這些log資訊,產生完,看看、確認過後就不再用了,就真的可以讓他閃過了無痕。也不需要存在硬碟裡浪費空間。 當然這樣的logger,訊息量有時候會很大,console如果buffer 不夠,在螢幕中也時常會一起閃過了無痕,所以看狀況,有時也會放到file裡去。

log4j.org.springframework=debug, spring
log4j.appender.spring=org.apache.log4j.ConsoleAppender

對外間接的程式碼中,使用中等級的logger,並保留到實體儲存體

有時候,我會在與對外系統間接的地方安插logger。這樣的logger,主要是用作稽核,確定傳出去的資料或是接受到的資料是正確的,以釐清資料流責任。如果是外部系統傳入了與規格不合的資料,而導致回傳結結果錯誤,或甚至系統錯錯誤,馬上就可以提出證據,立馬將責任丟出去, XD。 這樣的logger,我通常都會使用info等級,並存放在DAO中,因為通常與外部系統間接的地方,都是在DAO。

規格不確定的地方 使用略高等級

比方說設計階段明明說好了這個變數只有4種值,執行時或上線後卻跑出了第5種,這時候我除了會做error handle(可能會讓程式繼續跑下去),也會使用略高的訊息輸出等級來記錄訊息, 這樣可以比較一目了然。 另外像基本資料、或是前提假這類的資訊,如果handle 到error,我也會使用略高等級的等級來紀錄。

稽核型態的logger,會使用中高等級來記錄訊息

比方說如果我想監控系統使用人員,可能就會使用這樣的logger:

logger.warn("一個人從"+ logFromIp+ "執行了"+requestUrl);

如同之前稽核作用的log,這樣的log訊息,自然也是會放在實體儲存體中。

我在前面有的一些地方,有提到實體儲存體,而不使用檔案,是因為有時候可以依情況,將log儲入檔案或是DB。

Exception使用error訊息等級

Try-catch的地方,我一般會使用error訊息等級來記錄Exception;而且Exception都會記錄stacktrace。 在Layout的部份,也都會儘量可以指出程式碼行號。 指出程式碼行號有2個好處: 第一,可以很明確的指出錯誤拋出的程式碼。 第二,如果你發現訊息的行號與程式的行號不一致,這只表示一件是: 你現在看的程式碼與實際執行的程式是不同的。 例如:這個訊息在程式碼中,明明是第3行,可是log卻說在第5行,這表示你上錯檔案、或是執行到不同版本的程式。

3 logger.info("這個訊息應在第三行");

[FOO] 20:44:35 105 info Test:5 - 這個訊息應在第三行

開發時期資訊使用低訊息等級

一般來說IDE的Debug模式是可以顯示執行中變數的值,但這通常都是限制執行環境必需要在IDE中才可以。不過當你無法在IDE中執行的時候,就必需要靠傳統的「插旗標」的方法,來顯示變數值。通常像「插旗標」用的log,我都會使用最低等級來顯示─debug,或甚至是trace。 有時候,在追蹤程式的流程時,也是同樣會使用低級的logger。

logger.tracert("變數A:" + a );
logger.debug("取得資料庫連線");

因為這些情況,大多在開發時期所會需要使用到的資訊,不需要在上線時也浪費效能與系統資源來顯示這些訊。 簡單來說,開發時期所需要用的資訊,我都會使用低訊息等級來輸出

依照不同的環境,有不同的配置檔

例如在開發環境下,我會將輸出全部導到console上, 而在正式環境中,則將輸出全部導到檔案,或是資料庫中。 當然,這必需搭配相關的佈署策略與工具,可以使用相同的source code,依不同的執行命令或參數,使用不同的設定檔,產生2種不同的war檔。 實務上的作法,當然是另外一篇文章的事。 如果輸出是檔案的話,也一般都會設成一天一個檔

結語

這篇文章大概介紹了logger的組成成份,還有對我平常如何使用logger的一些經驗匯整。 使用方式並不是絕對,只是我個人的經驗與看法,有其他的方式或經驗也歡迎大家提出來一起討論。

沒有留言 :

您或許對這些有興趣

Related Posts with Thumbnails

最後

謝謝您的閱讀,希望您可以有豐富的收獲。