與一個簡單的算法不一樣,一個合格的系統不只僅要求具備運行的高效和計算的準確,同時又必須兼顧穩定性、可靠性。其次,對於開發人員來講,又必須具備可拓展性和可維護性。各方面都必須很完善,這樣的一個系統才能稱得上是一個合格完美的系統。簡單的站在開發人員的角度分析,比較重視的是系統的可維護性,畢竟開發人員直面的是系統的代碼實現。一個代碼結構冗雜、模塊設計混亂、命名「異想天開」的系統對於開發者來講簡直到了咬牙切齒的地步!不能忍!堅定不能忍!因此在平時的開發過程當中就要時刻注意着系統的實現機制,從宏觀設計和微觀實現上面同時進行精雕細琢。前幾天看到阿里巴巴出的《Java 開發規範》,這裏附上連接,建議你們看看。java
說到可維護性及不得不涉及到系統監控和Bug的快速定位。在開發階段還比較容易對系統進行監控,通常都會在本機上對系統的運行進行實時監控。而對於bug的定位,開發者都會熟練使用debug功能進行bug定位,更有甚者經過多年的開發經驗根據系統的異常信息直接能分析出來Bug產生的緣由、位置以及解決方案。可是,系統畢竟是人開發的,咱們沒法預料到在運行中會出行什麼想不到的問題,即便在各類測試中沒有出現,可是也沒法保證不會出現一些意想不到的問題。那麼在系統運行期間若是產生問題出現異常且沒法在測試環境中重現,咱們又該如何快速、準確地對bug進行定位分析和解決呢?舉個親身的例子吧,公司一套設備監控系統,用來對上萬個節點進行實時監控,若是該節點有異常(好比溫度太高、電壓太高等)則向系統進行發出告警信息。在開發環境中只有五十多個設備被安全(不會產生什麼告警)的放在機房中供開發和測試使用,這種測試環境根本沒法模擬實際環境。系統在測試中沒有出現過什麼大的問題。然而,在實際的運行環境中,偶爾發現系統的一個模塊功能會喪失,失去告警接收的功能。在本地測試的時候歷來沒有發現過相似問題,可是部署在實際環境中就會有發生。咱們不可能實時的24小時對系統進行人工監控,那麼該如何定位功能喪失的緣由呢?這時,對於系統日誌來講就「是時候表演真正的技術了」。後來經過系統日誌分析以後發現功能的喪失是因爲大量的告警同時上報致使數據庫連接不夠用產生大量connectionTimeout進而致使OOM異常,這個模塊完全死了。可見,日誌對於運行環境中系統的監控和問題定位是相當重要的,在系統設計、開發和實現的過程當中必須時刻注意着log的輸出,這將會對於往後的系統監控和異常分析起相當重要的做用!mysql
那麼,在系統日誌實現上應該注意哪些問題呢?如何作到不濫用日誌、減小大量無用信息,讓日誌記錄足夠精簡明瞭?算法
第一:系統的哪些運行信息須要進行日誌記錄?spring
一、功能模塊的啓動和結束(完整的系統由多個功能模塊組成,每一個模塊負責不一樣的功能,所以須要對模塊的啓動和結束進行監控。是否在須要的時機正常加載該模塊?又是否在退出結束的時候正常完成結束操做,正常退出?)sql
二、用戶的登陸和退出(哪位用戶在什麼時間經過什麼IP登陸或退出了系統)數據庫
三、系統的關鍵性操做(數據庫連接信息、網絡通訊的成功與失敗等)apache
四、系統運行期間的異常信息(NPE、OOM以及其餘的超時、轉換異常等)安全
五、關鍵性方法的進入和退出(一些重要業務處理的方法,在進入和結束的時候須要有日誌信息進行輸出)網絡
……多線程
……
第二:什麼樣的日誌格式有助於開發者進行明確的分析?
日誌信息要求必須精簡,過多的無用信息不但對系統分析起不到什麼做用,反而會增長系統的運行壓力、消耗系統的運行資源。這裏有個日誌模板,可供參考。
時間-[線程名][日誌等級]-日誌輸出位置(全類名,能夠精確到方法名):日誌信息
2013-09-04 10:49:20.296-[Thread-initRedis21504][INFO]-com.shanghai.LoginController.initLogInfo:LingMing[User] is logining
日誌信息的內容能夠根據不一樣的狀況進行設計,可是前面的時間到日誌輸出位置必需要保證完整性,這樣纔有利於日誌的分析。
第三:如何對不一樣的日誌信息進行等級劃分?
日誌等級一般分爲四種:DEBUG、INFO、WARN、ERROR
DEBUG:系統調試信息,一般用於開發過程當中對系統運行狀況的監控,在實際運行環境中不進行輸出。
INFO:系統運行的關鍵性信息,一般用於對系統運行狀況的監控。
WARN:告警信息,系統存在潛在的問題,有可能引發運行異常,但此時並未產生異常。
ERROR:系統錯誤信息,須要進行及時處理和優化。
這裏列出來了各類等級的日誌信息,在開發過程當中哪些信息須要設置爲哪一種等級有賴於開發者的本身判斷,這裏只是給個建議。
日誌的管理是系統很重要的一部分,千萬不可忽略其重要性。完整的日誌將會在系統維護中起着異常重要的做用,就好像磨刀不誤砍柴工同樣,日誌就像對系統進行分析的工具,工具便捷了,對系統分析起來就能達到事半功倍的效果。開發者必需要明白日誌的價值和意義,萬萬不可忽略和輕視,而且在系統設計之初就建議制定一份關於日誌管理的說明規範,明確哪些方法、操做必須進行日誌輸出,在進行開發過程當中也要嚴格遵照。
至於日誌輸出的實現,不一樣的開發語言有不一樣的日誌管理框架,同一種語言也有不少不一樣的日誌管理方案,這裏就再也不進行贅述。本文目的就是想讓開發者看到日誌的重要性。
下面附帶一份log4j的配置文件,便於往後進行設計的時候查找。轉載自:log4j.properties配置詳解與實例-所有測試經過
################################################################################ #①配置根Logger,其語法爲: # #log4j.rootLogger = [level],appenderName,appenderName2,... #level是日誌記錄的優先級,分爲OFF,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,ALL ##Log4j建議只使用四個級別,優先級從低到高分別是DEBUG,INFO,WARN,ERROR #經過在這裏定義的級別,您能夠控制到應用程序中相應級別的日誌信息的開關 #好比在這裏定義了INFO級別,則應用程序中全部DEBUG級別的日誌信息將不被打印出來 #appenderName就是指定日誌信息輸出到哪一個地方。可同時指定多個輸出目的 ################################################################################ ################################################################################ #②配置日誌信息輸出目的地Appender,其語法爲: # #log4j.appender.appenderName = fully.qualified.name.of.appender.class #log4j.appender.appenderName.optionN = valueN # #Log4j提供的appender有如下幾種: #1)org.apache.log4j.ConsoleAppender(輸出到控制檯) #2)org.apache.log4j.FileAppender(輸出到文件) #3)org.apache.log4j.DailyRollingFileAppender(天天產生一個日誌文件) #4)org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件) #5)org.apache.log4j.WriterAppender(將日誌信息以流格式發送到任意指定的地方) # #1)ConsoleAppender選項屬性 # -Threshold = DEBUG:指定日誌消息的輸出最低層次 # -ImmediateFlush = TRUE:默認值是true,全部的消息都會被當即輸出 # -Target = System.err:默認值System.out,輸出到控制檯(err爲紅色,out爲黑色) # #2)FileAppender選項屬性 # -Threshold = INFO:指定日誌消息的輸出最低層次 # -ImmediateFlush = TRUE:默認值是true,全部的消息都會被當即輸出 # -File = C:\log4j.log:指定消息輸出到C:\log4j.log文件 # -Append = FALSE:默認值true,將消息追加到指定文件中,false指將消息覆蓋指定的文件內容 # -Encoding = UTF-8:能夠指定文件編碼格式 # #3)DailyRollingFileAppender選項屬性 # -Threshold = WARN:指定日誌消息的輸出最低層次 # -ImmediateFlush = TRUE:默認值是true,全部的消息都會被當即輸出 # -File = C:\log4j.log:指定消息輸出到C:\log4j.log文件 # -Append = FALSE:默認值true,將消息追加到指定文件中,false指將消息覆蓋指定的文件內容 # -DatePattern='.'yyyy-ww:每週滾動一次文件,即每週產生一個新的文件。還能夠按用如下參數: # '.'yyyy-MM:每個月 # '.'yyyy-ww:每週 # '.'yyyy-MM-dd:天天 # '.'yyyy-MM-dd-a:天天兩次 # '.'yyyy-MM-dd-HH:每小時 # '.'yyyy-MM-dd-HH-mm:每分鐘 # -Encoding = UTF-8:能夠指定文件編碼格式 # #4)RollingFileAppender選項屬性 # -Threshold = ERROR:指定日誌消息的輸出最低層次 # -ImmediateFlush = TRUE:默認值是true,全部的消息都會被當即輸出 # -File = C:/log4j.log:指定消息輸出到C:/log4j.log文件 # -Append = FALSE:默認值true,將消息追加到指定文件中,false指將消息覆蓋指定的文件內容 # -MaxFileSize = 100KB:後綴能夠是KB,MB,GB.在日誌文件到達該大小時,將會自動滾動.如:log4j.log.1 # -MaxBackupIndex = 2:指定能夠產生的滾動文件的最大數 # -Encoding = UTF-8:能夠指定文件編碼格式 ################################################################################ ################################################################################ #③配置日誌信息的格式(佈局),其語法爲: # #log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class #log4j.appender.appenderName.layout.optionN = valueN # #Log4j提供的layout有如下幾種: #5)org.apache.log4j.HTMLLayout(以HTML表格形式佈局) #6)org.apache.log4j.PatternLayout(能夠靈活地指定佈局模式) #7)org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串) #8)org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等信息) #9)org.apache.log4j.xml.XMLLayout(以XML形式佈局) # #5)HTMLLayout選項屬性 # -LocationInfo = TRUE:默認值false,輸出java文件名稱和行號 # -Title=Struts Log Message:默認值 Log4J Log Messages # #6)PatternLayout選項屬性 # -ConversionPattern = %m%n:格式化指定的消息(參數意思下面有) # #9)XMLLayout選項屬性 # -LocationInfo = TRUE:默認值false,輸出java文件名稱和行號 # #Log4J採用相似C語言中的printf函數的打印格式格式化日誌信息,打印參數以下: # %m 輸出代碼中指定的消息 # %p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL # %r 輸出自應用啓動到輸出該log信息耗費的毫秒數 # %c 輸出所屬的類目,一般就是所在類的全名 # %t 輸出產生該日誌事件的線程名 # %n 輸出一個回車換行符,Windows平臺爲「\r\n」,Unix平臺爲「\n」 # %d 輸出日誌時間點的日期或時間,默認格式爲ISO8601,也能夠在其後指定格式 # 如:%d{yyyy年MM月dd日 HH:mm:ss,SSS},輸出相似:2012年01月05日 22:10:28,921 # %l 輸出日誌事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數 # 如:Testlog.main(TestLog.java:10) # %F 輸出日誌消息產生時所在的文件名稱 # %L 輸出代碼中的行號 # %x 輸出和當前線程相關聯的NDC(嵌套診斷環境),像java servlets多客戶多線程的應用中 # %% 輸出一個"%"字符 # # 能夠在%與模式字符之間加上修飾符來控制其最小寬度、最大寬度、和文本的對齊方式。如: # %5c: 輸出category名稱,最小寬度是5,category<5,默認的狀況下右對齊 # %-5c:輸出category名稱,最小寬度是5,category<5,"-"號指定左對齊,會有空格 # %.5c:輸出category名稱,最大寬度是5,category>5,就會將左邊多出的字符截掉,<5不會有空格 # %20.30c:category名稱<20補空格,而且右對齊,>30字符,就從左邊交遠銷出的字符截掉 ################################################################################ ################################################################################ #④指定特定包的輸出特定的級別 #log4j.logger.org.springframework=DEBUG ################################################################################ #OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL log4j.rootLogger =ALL,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB #輸出到控制檯 log4j.appender.systemOut = org.apache.log4j.ConsoleAppender log4j.appender.systemOut.layout = org.apache.log4j.PatternLayout log4j.appender.systemOut.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n log4j.appender.systemOut.Threshold = DEBUG log4j.appender.systemOut.ImmediateFlush = TRUE log4j.appender.systemOut.Target = System.out #輸出到文件 log4j.appender.logFile = org.apache.log4j.FileAppender log4j.appender.logFile.layout = org.apache.log4j.PatternLayout log4j.appender.logFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logFile.Threshold = DEBUG log4j.appender.logFile.ImmediateFlush = TRUE log4j.appender.logFile.Append = TRUE log4j.appender.logFile.File = ../Struts2/WebRoot/log/File/log4j_Struts.log log4j.appender.logFile.Encoding = UTF-8 #按DatePattern輸出到文件 log4j.appender.logDailyFile = org.apache.log4j.DailyRollingFileAppender log4j.appender.logDailyFile.layout = org.apache.log4j.PatternLayout log4j.appender.logDailyFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logDailyFile.Threshold = DEBUG log4j.appender.logDailyFile.ImmediateFlush = TRUE log4j.appender.logDailyFile.Append = TRUE log4j.appender.logDailyFile.File = ../Struts2/WebRoot/log/DailyFile/log4j_Struts log4j.appender.logDailyFile.DatePattern = '.'yyyy-MM-dd-HH-mm'.log' log4j.appender.logDailyFile.Encoding = UTF-8 #設定文件大小輸出到文件 log4j.appender.logRollingFile = org.apache.log4j.RollingFileAppender log4j.appender.logRollingFile.layout = org.apache.log4j.PatternLayout log4j.appender.logRollingFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logRollingFile.Threshold = DEBUG log4j.appender.logRollingFile.ImmediateFlush = TRUE log4j.appender.logRollingFile.Append = TRUE log4j.appender.logRollingFile.File = ../Struts2/WebRoot/log/RollingFile/log4j_Struts.log log4j.appender.logRollingFile.MaxFileSize = 1MB log4j.appender.logRollingFile.MaxBackupIndex = 10 log4j.appender.logRollingFile.Encoding = UTF-8 #用Email發送日誌 log4j.appender.logMail = org.apache.log4j.net.SMTPAppender log4j.appender.logMail.layout = org.apache.log4j.HTMLLayout log4j.appender.logMail.layout.LocationInfo = TRUE log4j.appender.logMail.layout.Title = Struts2 Mail LogFile log4j.appender.logMail.Threshold = DEBUG log4j.appender.logMail.SMTPDebug = FALSE log4j.appender.logMail.SMTPHost = SMTP.163.com log4j.appender.logMail.From = xly3000@163.com log4j.appender.logMail.To = xly3000@gmail.com #log4j.appender.logMail.Cc = xly3000@gmail.com #log4j.appender.logMail.Bcc = xly3000@gmail.com log4j.appender.logMail.SMTPUsername = xly3000 log4j.appender.logMail.SMTPPassword = 1234567 log4j.appender.logMail.Subject = Log4j Log Messages #log4j.appender.logMail.BufferSize = 1024 #log4j.appender.logMail.SMTPAuth = TRUE #將日誌登陸到MySQL數據庫 log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender log4j.appender.logDB.layout = org.apache.log4j.PatternLayout log4j.appender.logDB.Driver = com.mysql.jdbc.Driver log4j.appender.logDB.URL = jdbc:mysql://127.0.0.1:3306/xly log4j.appender.logDB.User = root log4j.appender.logDB.Password = 123456 log4j.appender.logDB.Sql = INSERT INTOT_log4j(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)values('Struts2','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')