有效地記錄日志可以簡(jiǎn)化企業(yè)的開(kāi)發(fā)過(guò)程提前規劃一個(gè)記錄日志的計劃,在開(kāi)發(fā)過(guò)程后期就可以獲益 ![]() |
![]() |
級別: 中級 Charles Chan , 首席顧問(wèn), Ambrose Software Inc. 2005 年 9 月 05 日 在企業(yè)級的開(kāi)發(fā)過(guò)程中,我們不可避免地會(huì )碰到很多問(wèn)題;如果您希望在開(kāi)發(fā)過(guò)程的后期能夠有效地捕捉 bug,那就需要一種有效的日志策略。但是在一個(gè)企業(yè)的應用程序中要想實(shí)現有效地記錄日志,需要進(jìn)行一番規劃,并設計一些準則。在本文中,顧問(wèn) Charles Chan 將向您介紹一些最好的實(shí)踐,從而幫助您從項目一開(kāi)始就編寫(xiě)有用的日志代碼。 如果您是一名開(kāi)發(fā)人員,那您很可能就已經(jīng)具有這種經(jīng)驗:您已經(jīng)開(kāi)發(fā)了一些代碼以及一些測試用例。應用程序經(jīng)過(guò)了嚴格的 QA 測試,您確信代碼可以完全適合業(yè)務(wù)的需求。然而,在將應用程序最終交付終端用戶(hù)的手里時(shí),卻會(huì )出現一些預想不到的問(wèn)題。如果沒(méi)有適當的日志消息,可能需要花費幾天的時(shí)間來(lái)診斷這些問(wèn)題。不幸的是,大部分項目對于日志都沒(méi)有一個(gè)清晰的策略。如果沒(méi)有這種策略,系統產(chǎn)生的日志消息就有可能無(wú)益于問(wèn)題的分析和解決。在本文中,我們將討論企業(yè)應用程序日志的各個(gè)方面的問(wèn)題。您將看到一個(gè) Java™ 平臺上日志 API 的概述,學(xué)習一些最好的編寫(xiě)日志代碼的實(shí)踐,并了解如果需要在產(chǎn)品環(huán)境中對詳細日志重新進(jìn)行排序,應該如何處理。 選擇日志 API 在使用 Java 平臺進(jìn)行開(kāi)發(fā)時(shí),可以使用兩個(gè)主要的日志 API:Apache Log4J 和 Java Logging API,在 1.4 及更高版本的 Java 平臺中都提供了這兩個(gè) API。與 Java Logging API 相比,Log4J 更加成熟,特性也更加豐富。這兩個(gè)日志的實(shí)現都采用了一個(gè)類(lèi)似的設計模式(如圖 1 所示)。除非您的公司限制要使用第三方的庫,否則我強烈建議使用 Log4J。如果您不能決定使用哪個(gè) API,就可以使用 Apache Commons Logging API,它對底層的日志實(shí)現進(jìn)行了封裝。從理論上來(lái)說(shuō),這樣不用修改代碼就可以進(jìn)行日志實(shí)現的切換。然而,實(shí)際上您很少會(huì )切換日志的實(shí)現;因此,我不建議使用 Apache Commons Logging API,因為它的復雜性并不沒(méi)有給您帶來(lái)其他特性。
日志概述 Log4J 和 Java Logging API 都采用了類(lèi)似的設計和使用模式(如圖 1 和清單 1 所示)。消息首先被創(chuàng )建,然后傳遞給一個(gè)具有特定優(yōu)先權的日志對象。這些消息的目的和格式是由輸出處理程序及其布局所決定。 圖 1. 日志實(shí)現的主要組件 ![]() 清單 1. 日志對象的實(shí)例化和使用
一個(gè)好的日志實(shí)現中提供了很多不同的輸出處理程序,最常見(jiàn)的文件輸出處理程序和終端輸出處理程序。Log4J 還提供了一些處理程序將消息發(fā)布到一個(gè) JMS 主題中,或者將消息插入一個(gè)數據庫表中。盡管這編寫(xiě)一個(gè)定制的附加器并不困難,但是編寫(xiě)和維護這種代碼的總體成本不應低估。消息的格式可以通過(guò) 清單 2 給出了一個(gè) Log4J 的樣例配置文件,它負責配置 清單 2. Log4J XML 配置樣例文件
日志最佳實(shí)踐 關(guān)于日志,您要做的一個(gè)最重要的選擇可能是確定一種模式,將每個(gè)日志消息分配給一個(gè)特定的 類(lèi)別。常見(jiàn)的一種實(shí)踐是使用每個(gè)類(lèi)的全名,這些類(lèi)的操作會(huì )被作為一個(gè)消息類(lèi)別在日志中記錄(正如我們在清單 1 中看到的一樣),這是因為這可以讓開(kāi)發(fā)人員更細粒度地記錄每個(gè)類(lèi)的設置。然而,這只有在使用日志消息來(lái)跟蹤執行過(guò)程時(shí)才能良好地工作。在企業(yè)級的應用程序中,有很多其他類(lèi)型的日志消息。舉例來(lái)說(shuō),一條日志消息可能是為安全顧問(wèn)產(chǎn)生的,而另外一條日志消息則可能是會(huì )為了幫助進(jìn)行性能調優(yōu)而產(chǎn)生的。如果這兩條消息所關(guān)注的是同一個(gè)類(lèi),這樣就會(huì )被分配給相同的類(lèi)別,這將很難在日志輸出結果中對其進(jìn)行區分。 為了避免這個(gè)問(wèn)題,應用程序應該具有一組專(zhuān)用的日志記錄程序,它們都進(jìn)行了獨特的分類(lèi),如清單 3 所示。每個(gè)日志記錄程序都可以配置自己的優(yōu)先級和輸出處理程序。例如,安全性日志記錄程序可以在將日志寫(xiě)入目的地之前對消息進(jìn)行加密。有時(shí)應用程序的設計者應該與使用日志的用戶(hù)(例如安全顧問(wèn))一起來(lái)商討日志的輸出格式,從而對這些消息進(jìn)行更好的控制。 清單 3. 專(zhuān)用的日志記錄程序
選擇日志的級別 一個(gè) 類(lèi)別 (例如 security)中的消息可以具有不同的 優(yōu)先級。有些消息是為了調試而產(chǎn)生的,有些是為了警告而產(chǎn)生的,有些則是出現錯誤而產(chǎn)生的。消息的不同優(yōu)先級可以通過(guò)記錄 級別 來(lái)產(chǎn)生。最常用的日志級別有:
標準的 Java Logging API 和 Apache Log4J 在此之外又提供了一些日志級別。日志級別的主要目標是幫助您過(guò)濾有用信息中的噪聲。為了防止出現使用錯誤的級別以及降低日志消息的效用的情況,在開(kāi)始編碼之前,必須為開(kāi)發(fā)人員提供一個(gè)清晰的指導方針。 日志消息的格式 一旦選定日志記錄程序并建立起日志級別之后,就可以開(kāi)始構建日志消息了。在這樣做時(shí),重要的是要包含盡可能多的上下文信息,例如用戶(hù)提供的參數,其他應用程序的狀態(tài)信息。記錄日志對象的一種方法是將它們轉換成 XML。第三方庫,例如 XStream(請參閱 參考資料)可以自動(dòng)將 Java 對象轉換成 XML 。盡管這是一種非常強大的機制,但是我們必須要考慮在速度與詳細程度之間達到一種平衡。除了典型的應用程序狀態(tài)信息之外,還應該記錄以下信息:
上面這些信息(除了調用程序標識)都是由日志實(shí)現自動(dòng)獲取的。為了將這些信息包含到消息中,您只需要為輸出處理程序配置一個(gè)適當的 layout 模式即可。要捕獲調用者的標識,您可以利用 Log4J 中的診斷上下文特性(更多信息請參閱 參考資料)。診斷上下文讓您可以將上下文信息與當前正在運行的線(xiàn)程關(guān)聯(lián)在一起。這些信息可以在為輸出進(jìn)行格式化的同時(shí)而包含到每條消息中。 在 J2EE Web 應用程序中,應用邏輯將用戶(hù)標識保存到診斷上下文中最好的地方是在一個(gè) servlet 過(guò)濾器中。清單 4 中顯示了要實(shí)現這種功能的必要代碼。它使用了 Log4J 1.3 alpha 中提供的映射診斷上下文類(lèi)( 清單 4. 在 servlet 過(guò)濾器中使用診斷上下文
使用 AspectJ 跟蹤執行情況 在對問(wèn)題進(jìn)行診斷時(shí),通常跟蹤程序的執行情況會(huì )很有幫助。您可以在程序執行的不同地方持續發(fā)送日志消息嗎?例如方法的入口函數和出口函數。這是一個(gè)老問(wèn)題,在出現 AspectJ 之前一直都沒(méi)有什么好的解決方案。使用 AspectJ,可以在應用程序的不同地方執行代碼段。在 AspectJ 中,這些地方都稱(chēng)為 point cut,在 point cut 處所執行的代碼稱(chēng)為 advice。point cut 和advice 合稱(chēng) aspect。 關(guān)于 AspectJ,有一件事情非常神奇,aspect 不用很多努力就可以應用到整個(gè)應用程序中。有關(guān) AspectJ 的更多信息,請參閱 參考資料。清單 5 給出了一個(gè) AspectJ 源文件的例子,它用來(lái)對方法的入口和出口函數記錄日志。在這個(gè)例子中,跟蹤日志程序將在每次進(jìn)入或退出 清單 5. 使用 AspectJ 記錄方法的入口和出口
產(chǎn)品環(huán)境中的日志 一旦應用程序處于產(chǎn)品環(huán)境中之后,您通常都需要關(guān)閉調試或信息日志消息,從而對運行時(shí)的性能進(jìn)行優(yōu)化。然而,當有些不好的事情發(fā)生時(shí),您又不能在開(kāi)發(fā)環(huán)境中重現這個(gè)問(wèn)題,那就可能需要在產(chǎn)品環(huán)境中激活調試消息了。重要的是能夠修改日志的設置,而不用關(guān)閉服務(wù)器。診斷產(chǎn)品的問(wèn)題即使不用花費數天來(lái)進(jìn)行詳細的調研,通常也需要幾個(gè)小時(shí)的時(shí)間。在這段時(shí)間之內,開(kāi)發(fā)人員需要激活或關(guān)閉應用程序不同范圍的日志。如果每次修改日志的設置之后都需要重新啟動(dòng)產(chǎn)品應用程序,那么情況就會(huì )變得非常不可靠了。 幸運的是,Log4J 提供了一種簡(jiǎn)單的機制來(lái)解決這個(gè)問(wèn)題。在 Log4J 1.2 中, 為了確保 清單 6. 使用 DOMConfigurator 配置 Log4J
如果您的日志配置文件不能方便地進(jìn)行訪(fǎng)問(wèn)(例如您的產(chǎn)品環(huán)境是由一個(gè)不同的組織進(jìn)行維護的),那么您就必須使用一種不同的策略。標準的方法是使用 JMX,它提供了一個(gè)標準的 API 來(lái)管理自己的應用程序設置。在現代 JMX 兼容的服務(wù)器中,您可以使用管理 bean (或 MBeans )來(lái)擴展應用服務(wù)器的管理終端的功能(更多有關(guān)使用 JMX 以及在 WebSphere Application Server 6.0 中使用 JMX 的內容,請參閱 參考資料 一節。)由于 JMX 方法非常復雜,如果您的情況需要使用 JMX,那就應該只用作這個(gè)用途。 記錄敏感的數據 在記錄產(chǎn)品環(huán)境中的日志時(shí),除了技術(shù)方面的挑戰之外,還存在一些業(yè)務(wù)問(wèn)題需要克服。例如,記錄敏感的信息可能會(huì )引起安全性的問(wèn)題。并沒(méi)有任何限制可以防止您將某個(gè)用戶(hù)的用戶(hù)名和密碼保存到正文文件中。您還必須要保護其他敏感信息,例如 e-mail 地址、電話(huà)號碼以及賬號信息。安全顧問(wèn)和設計師有責任要確保這些信息不會(huì )未加任何處理就保存到日志中。對敏感信息使用安全性專(zhuān)用的日志程序可以幫助降低風(fēng)險。您可以給這個(gè)日志程序配置一個(gè)專(zhuān)用的附加器,從而使用一種加密的格式來(lái)保存消息,或者將其保存到一個(gè)安全的地方。然而,防止出現安全風(fēng)險的最佳方法是在項目開(kāi)始之前就設置適當的編碼規范,并在檢查代碼時(shí)強制施行這些規范。 從異常中提取有用信息 當發(fā)生一個(gè)非預期的異常時(shí) —— 例如,如果數據庫連接突然失效了,或者系統資源變得很低了 —— 就必須對其適當地進(jìn)行處理,否則就會(huì )丟失有用的信息,這些信息在診斷問(wèn)題時(shí)是非常有幫助的。首先,必須記錄異常及其堆棧跟蹤狀況。其次,應該使用一種用戶(hù)界面友好的方式來(lái)標識錯誤頁(yè)面,這對于終端用戶(hù)和技術(shù)支持小組來(lái)說(shuō)都是非常有幫助的。 技術(shù)支持小組在接到一個(gè)技術(shù)支持電話(huà)時(shí)所面臨的一個(gè)挑戰是在用戶(hù)所報告的問(wèn)題與特定的日志異常之間建立某種關(guān)聯(lián)。非常有用的一種簡(jiǎn)單技術(shù)是為每個(gè)異常都記錄一個(gè)唯一的 ID。這個(gè) ID 可以告訴用戶(hù),也可以包含在終端用戶(hù)所填寫(xiě)的問(wèn)題報告表單中。這樣可以減少技術(shù)支持團隊成員猜測的時(shí)間,讓他們可以快速對問(wèn)題作出響應??紤]到可讀性的問(wèn)題,可以定期對 ID 進(jìn)行回收。 日志文件的管理 一個(gè)非常繁忙的應用程序的日志文件可能會(huì )迅速變得非常大。較大的日志文件很難使用,這是因為它們需要過(guò)濾大量的噪聲才能找到有用的信號。Log 循環(huán) 是常見(jiàn)的一個(gè)可以幫助解決這個(gè)問(wèn)題的實(shí)踐。日志循環(huán)會(huì )周期性地對舊日志進(jìn)行歸檔,這樣新消息就可以總能寫(xiě)到一個(gè)相對較小的文件中。日志消息降低了一些效用來(lái)提高速度;您可能很少需要參考一周之前的日志消息。在 Log4J 1.2 中, 清單 7. 使用 DailyRollingFileAppender 循環(huán)使用日志文件
集群環(huán)境中的日志 現在有越來(lái)越多的企業(yè)級應用程序是在集群環(huán)境或分布式環(huán)境中進(jìn)行部署的。然而,集群環(huán)境中的日志需要更多規劃,因為消息都是從不同的源頭生成的(通常是不同的機器)。如果要對不同的機器記錄日志,那就必須對這些機器的時(shí)間戳進(jìn)行同步,否則日志消息的次序就混亂了。對機器間時(shí)鐘進(jìn)行同步的一種簡(jiǎn)單方法是使用一個(gè)時(shí)間服務(wù)器。有兩種方法可以設置時(shí)間服務(wù)器。您可以指定一臺內部的機器作為時(shí)間服務(wù)器。然后其他機器就可以使用網(wǎng)絡(luò )時(shí)間協(xié)議(NTP)來(lái)與時(shí)間服務(wù)器的時(shí)間戳進(jìn)行同步。另外,您可以使用 Internet 上提供的時(shí)間服務(wù)器(請參閱 參考資料)。在 AIX 上, 在集群環(huán)境中搜集日志消息還面臨著(zhù)一些挑戰。在這種環(huán)境中保存日志消息的一種簡(jiǎn)單方法是將它們保存到主機特定的日志文件中。當集群是使用 session affinity 配置時(shí),這可以很好地工作 —— 如果對某個(gè)特定用戶(hù)會(huì )話(huà)的請求都要到同一個(gè)服務(wù)器上,并且 EJB 也都是部署在本地的。在這種配置中,集群中的機器所產(chǎn)生的日志文件都可以獨立進(jìn)行分析。如果不是這種情況 —— 換而言之,如果任何給定的請求都可以由多臺機器進(jìn)行處理 —— 那么對不同日志文件中的日志消息進(jìn)行分析就會(huì )變得更加困難。在這種情況中,一種好的辦法是使用系統管理軟件來(lái)管理日志消息,例如 IBM Tivoli® 軟件(請參閱 參考資料 中的鏈接)。這種軟件對所有的日志消息(在系統管理軟件的術(shù)語(yǔ)中稱(chēng)之為 事件)提供了一個(gè)綜合的視圖,從而便于管理員使用。系統管理軟件也可以根據所接收到的事件的類(lèi)型觸發(fā)一些操作(例如發(fā)送 e-mail 消息或傳呼消息)。
結束語(yǔ) 在本文中,我們介紹了在規劃日志策略時(shí)需要考慮哪些問(wèn)題。正如在編程時(shí)所碰到的問(wèn)題一樣,從一開(kāi)始就采用一個(gè)經(jīng)過(guò)詳細考慮的規劃要比在進(jìn)行的同時(shí)規劃更能節省工作量。良好的日志策略可以極大地幫助對問(wèn)題進(jìn)行診斷。最終,終端用戶(hù)可以獲得更好的應用程序,并能從技術(shù)支持團隊獲得迅速的響應。
參考資料
關(guān)于作者
| |||||||||||||||||||||||||||||||||||||||||||||||
聯(lián)系客服