徐萌飛 王軍玲
( 中國船舶重工集團公司第七一二研究所,武漢 430064 )
日志功能作為軟件系統(tǒng)的重要組成部分,在記錄軟件運行狀態(tài)、調(diào)測和故障定位方面發(fā)揮著重要的作用。在船舶系統(tǒng)中,控制系統(tǒng)要長期頻繁地和其他子系統(tǒng)進行信息交互??刂葡到y(tǒng)除了發(fā)出控制指令,還要從事被控制對象的狀態(tài)信息采集,檢測控制指令的執(zhí)行情況等多個動作,觸發(fā)事件多,實時性要求高,所以準(zhǔn)確高效的日志是控制系統(tǒng)必需功能。
在控制系統(tǒng)軟件的設(shè)計過程中,為滿足實際運行需求,日志功能要經(jīng)過全面設(shè)計,使用高效、可靠的實現(xiàn)方式,才能夠在實際運行中充分發(fā)揮其功用。本文將對日志記錄功能的軟件設(shè)計的若干要點及其實現(xiàn)方法進行探討,為后續(xù)軟件日志功能的設(shè)計和開發(fā)工作提供參考。
在軟件中,一般采用統(tǒng)一的日志記錄函數(shù)來完成日志功能,或者使用統(tǒng)一的對象來處理日志。要盡量避免在每一處需要記錄日志的地方單獨使用代碼實現(xiàn)日志功能。
使用統(tǒng)一的日志模塊具有如下優(yōu)勢:
(1)減少無價值的重復(fù)開發(fā)量
在日志中,每記錄一條日志都單獨編寫日志代碼,造成代碼冗余。而反復(fù)調(diào)用基本的輸入輸出語句(如文件的打開和關(guān)閉),導(dǎo)致效率低下。
(2)因需求變化導(dǎo)致的修改量小
采用共用日志模塊函數(shù)或?qū)ο蠓椒?,對日志記錄格式或者?nèi)容提出了新的需求,只需要一處修改,即可全面生效。而單獨使用代碼記錄的,可能需要處處修改。
日志作為記錄程序運行過程的載體,必須能夠完整記錄下運行時間,運行對象,執(zhí)行動作,執(zhí)行結(jié)果,執(zhí)行前狀態(tài)量,執(zhí)行后狀態(tài)量等信息,這樣在發(fā)生故障后才能追根溯源。國外已經(jīng)有很多成熟軟件和相應(yīng)的技術(shù)規(guī)范。
以UNIX下的syslog為例,它已經(jīng)形成了一種事實上的工業(yè)標(biāo)準(zhǔn),其日志記錄格式按RFC3164 (The BSD log protocol)定義,并對消息頭部進行擴展,其格式為:
<優(yōu)先級>時間戳 主機名 模塊名/級別/信息摘要:內(nèi)容
<priority>time stamp sysname module/level/digest:content
控制系統(tǒng)軟件的日志格式,可以參考這類方式實現(xiàn)。
在以往的日志功能設(shè)計過程中,往往容易遺漏對代碼執(zhí)行前狀態(tài)量的記錄,導(dǎo)致在回溯日志時會凸現(xiàn)其缺陷。例如:系統(tǒng)發(fā)生了無法重現(xiàn)的問題,如果從代碼邏輯上檢視不出問題,而又缺乏對執(zhí)行前系統(tǒng)狀態(tài)量的記錄,就不能確定代碼執(zhí)行前系統(tǒng)是否處于正常工作狀態(tài),也無法定位是哪部分子系統(tǒng)故障或者硬件功能失效。
在主控系統(tǒng)和各個子系統(tǒng)共同運行的情況下,尤其是聯(lián)調(diào)過程中,各個子系統(tǒng)不能因為主系統(tǒng)在記錄日志信息就放棄自身的日志功能。因為在主控系統(tǒng)和子系統(tǒng)接口信息傳遞過程中,子系統(tǒng)日志和主控系統(tǒng)日志之間可以相互印證。缺少了子系統(tǒng)的日志作為判斷依據(jù),會增加對消息傳遞通道以及子系統(tǒng)接口功能模塊的檢查成本。
控制系統(tǒng)的動作往往是具有時序性的,而且集散控制系統(tǒng)中,更是采用集中管理、分散控制,所以保證日志記錄精度對后期分析尤為重要。
當(dāng)控制系統(tǒng)啟動后,一個重要的原則是必須有一個可靠的時間基準(zhǔn),即統(tǒng)一的時間源。在多個子系統(tǒng)的動作具有時序關(guān)系或者相互影響的情況下,如果沒有統(tǒng)一的時間基準(zhǔn),將無法根據(jù)日志確定故障發(fā)生的時間,以及確定當(dāng)時各個系統(tǒng)所處的狀態(tài),更無法找到問題發(fā)生的根源。
建立統(tǒng)一時間基準(zhǔn)的方法有多種,在系統(tǒng)要求不高的情況下,一般可以利用系統(tǒng)內(nèi)部的晶振時鐘。精度要求高的情況下,可以引入外部高精度時鐘。外部時鐘的引入,可以通過外部時鐘硬件,也可以通過網(wǎng)絡(luò)獲取標(biāo)準(zhǔn)時間。
在與外界通訊隔絕的封閉式系統(tǒng)中,由于沒有獲取標(biāo)準(zhǔn)時間的條件,則需要盡可能將系統(tǒng)(一般是主控系統(tǒng))的初始上電時間作為其內(nèi)部時鐘的日志記錄起點時間。各子系統(tǒng)上電后也要通過通訊接口與主控系統(tǒng)進行時鐘同步,來保證后續(xù)日志記錄在時間維度上的一致性。
在控制系統(tǒng)對處理精度要求較高的情況下,有的子系統(tǒng)本身會帶有時鐘,雖然子系統(tǒng)可以靠自身的時鐘來記錄時間,但是在進行業(yè)務(wù)循環(huán)時,子系統(tǒng)仍需要在每次業(yè)務(wù)處理開始時,和主控系統(tǒng)進行一次時間再同步。如果不做這個同步的動作,那么在子系統(tǒng)時鐘和主控系統(tǒng)時鐘精度不同的情況下,隨著業(yè)務(wù)循環(huán)次數(shù)增加,時間偏差將積累擴大,一旦經(jīng)過多次業(yè)務(wù)循環(huán)后發(fā)生故障,則兩個系統(tǒng)之間日志記錄的時序匹配關(guān)系會發(fā)生偏差,會很大程度上干擾問題定位。
在故障日志記錄的功能設(shè)計中,都需要對錯誤進行編號和分類工作,分類的依據(jù)有多種,例如有的系統(tǒng)按不同軟件模塊產(chǎn)生的錯誤進行編號,有的按不同錯誤類型進行分類編號。開發(fā)人員定義了錯誤號后,在內(nèi)部要保留一份錯誤號列表,便于故障出現(xiàn)后的快速檢索。例如可以使用如表1格式:
表1 故障日志中錯誤的編號和分類
尤其需要注意的是,只要是能夠詳細(xì)區(qū)分的錯誤,應(yīng)當(dāng)使用不同的錯誤號進行標(biāo)識。否則故障發(fā)生后,沒有唯一的故障原因與之對應(yīng),會給定位帶來冗余的工作量。
最后,在多個進程同時運行的情況下,建議每個進程分別記錄自己的日志。如果多個進程使用同一個日志文件,往往導(dǎo)致資源沖突,并且各個進程記錄的日志相互交錯,不便于日后查看。
由于日志記錄了整個業(yè)務(wù)運行流程,所以未經(jīng)加密保護的日志可能被逆向分析或者被篡改,從而導(dǎo)致系統(tǒng)架構(gòu)和設(shè)計方面的泄密,甚至導(dǎo)致軟件系統(tǒng)被攻擊,給廠商帶來安全隱患和知識產(chǎn)權(quán)隱患。例如在電信行業(yè)曾經(jīng)出現(xiàn)過由于充值日志被逆向分析,導(dǎo)致充值卡被偽造盜用的情況。其次,由于日志透露了詳細(xì)運行信息,尤其是故障發(fā)生后,日志記錄功能為了便于后續(xù)定位,往往全面記錄故障發(fā)生時系統(tǒng)狀態(tài)和故障產(chǎn)生原因,一旦被他人獲取,可能給相關(guān)廠商帶來商業(yè)上的糾紛和損失。例如某些市場關(guān)系不好的客戶會根據(jù)日志提供的信息夸大造成的損失向供應(yīng)商提出索賠。在軍用方面,日志的泄露,不僅會導(dǎo)致系統(tǒng)設(shè)計缺陷暴露,還可能導(dǎo)致軍用設(shè)備運行狀態(tài)失密。例如,如果未被加密的日志中記錄了設(shè)備運行周期時間或GPS定位的位置信息,則一旦信息流失,可能導(dǎo)致設(shè)備性能參數(shù)被泄露或船舶運行航線被暴露。
故此,很多軟件中也開始增強了對日志的保護。不少廠商采用加密方式記錄日志,用普通軟件無法查看其內(nèi)容,必須通過廠商提供的專有軟件進行解密處理。
控制系統(tǒng)運行時,產(chǎn)生的日志和告警信息很多,如果都向上位機控制臺轉(zhuǎn)發(fā),存在如下弊端,
(1)重要信息往往被海量的普通信息所掩蓋,無法引起運維人員關(guān)注,增大了系統(tǒng)失控的概率;
(2)日志中大量出現(xiàn)的告警會引起用戶的反感,甚至給客戶帶來軟件質(zhì)量不穩(wěn)定的感覺。
所以,日志記錄使用分級和開關(guān)控制顯示已成為軟件設(shè)計的必要因素。國外成熟的系統(tǒng)軟件,都對日志和告警進行了分級定義,仍以syslog為例,將日志分為八種安全級別,分別代表不同的含義,如表2。
對于日志級別為0-3的消息,系統(tǒng)應(yīng)該以非常明顯的顯示方式和提醒方式將信息傳遞給運維人員,例如用紅色或大字體傳遞到上位機,同時發(fā)出聲音告警,甚至可以發(fā)送警告短信到運維人員手機,啟動呼叫中心來呼叫運維人員。
表2 八種安全級別日志的含義
對于3級以下的信息,可根據(jù)實際需求,使用開關(guān)進行控制日志顯示。例如處于5級的通知信息,就可以考慮在平時關(guān)閉顯示開關(guān)而不上傳到上位機,僅錄入日志文件。處于6-7級的信息,只在軟件調(diào)測或聯(lián)調(diào)故障定位時才打開記錄開關(guān),平時運行時關(guān)閉,軟件不會把調(diào)試信息記錄到文件中,從而節(jié)省了存儲空間和處理時間。
另外,隨著積累的日志不斷增加,未來對數(shù)據(jù)進行整理和分析的需要也會產(chǎn)生。對于長期運行的控制系統(tǒng),建議在記錄日志信息時使用標(biāo)準(zhǔn)化的格式,便于后續(xù)使用分析軟件做數(shù)據(jù)挖掘和智能分析。
由于控制系統(tǒng)軟件不是孤立對象,其運行與操作系統(tǒng)和系統(tǒng)所在的硬件平臺有密切的關(guān)系,尤其是嵌入式系統(tǒng),更需要仔細(xì)分析。
雖然目前計算機的存儲能力在不斷增強,但在很多情況下,從設(shè)備占用空間和成本考慮,還是有不少系統(tǒng)提供的存儲空間容量有限。如一些ARM板上使用的存儲芯片,只有幾 k的存儲空間,一旦記錄內(nèi)容過多,就可能造成空間不足而記錄失敗。在這種情況下,要考慮精簡日志內(nèi)容或者使用壓縮編碼格式減少空間占用。
此外,基于某些操作系統(tǒng)運行的軟件,如果長時間記錄日志,會導(dǎo)致日志文件過大,延長了后續(xù)寫日志時每次打開文件的速度。甚至當(dāng)文件大小超過了操作系統(tǒng)可以支持上限時,(例如某些版本的UNIX文件,如果不打開大文件開關(guān),會有2G大小的限制),可能會發(fā)生日志文件被破壞或者日志記錄丟失的情況。
故此,對長期運行且產(chǎn)生日志數(shù)據(jù)量大的系統(tǒng),要設(shè)計定期備份和壓縮打包機制,將歷史日志記錄轉(zhuǎn)存到系統(tǒng)外大容量介質(zhì)(如磁帶機)上。
一些存儲芯片或者操作系統(tǒng),在每次重新記錄數(shù)據(jù)時,使用的寫入機制不是清空機制而是復(fù)寫機制。即存儲介質(zhì)上的舊數(shù)據(jù)不被清除,而使用從首地址記錄,用新記錄來覆蓋掉原記錄。例如在某嵌入式軟件設(shè)計中,使用了flash芯片做日志存儲,整個業(yè)務(wù)處于不斷循環(huán)中。在每次循環(huán)開始時,都重新記錄日志。由于使用了覆寫機制,剛開始多次業(yè)務(wù)流程都是成功的,但在某次業(yè)務(wù)循環(huán)過程中發(fā)生了故障,系統(tǒng)日志記錄也被中斷,結(jié)果發(fā)現(xiàn)舊的運行成功的日志記錄的后半部分和新的業(yè)務(wù)運行的前半部分混淆在一起。加上只在每次業(yè)務(wù)循環(huán)開始時加上了時間戳,在后續(xù)日志記錄過程中沒有繼續(xù)加時間戳。故此,當(dāng)故障發(fā)生后,定位缺陷時,查看日志仍以為是一份成功的日志,無法確認(rèn)程序運行時發(fā)生故障的時間以及存在缺陷的代碼。如圖1所示。
圖1 存在缺陷的日志
為避免這種情況發(fā)生,一般建議在記錄每條日志信息時,帶上時間戳標(biāo)記。如圖2所示。
如果實在無法建立時間戳標(biāo)記,可以通過日志流水號來區(qū)分。具體做法是,在存儲區(qū)使用一個專用地址區(qū)來記錄當(dāng)前最大的日志流水號。在每次需要記錄日志時,將最大流水號累加,更新到專用地址區(qū),此最大流水號也包含在日志記錄中。這樣即使采用覆蓋式寫入的日志內(nèi)容,也能夠通過不同的流水號段來區(qū)分,如圖3所示。
圖2 帶時間戳標(biāo)記日志
圖3 流水號日志
日志模塊對此部分過程處理的流程圖如圖4。
在很多實時控制系統(tǒng)中,由于系統(tǒng)硬件性能的限制,隨著日志內(nèi)容的增加,記錄的讀寫速度會越來越低,而且頻繁打開和關(guān)閉文件也會占用系統(tǒng)資源。在這種情況下,也要考慮精簡日志,或者通過日志開關(guān)減少不重要的日志記錄動作。
可以考慮的一種設(shè)計是,在系統(tǒng)具有富余內(nèi)存時,可借助更高性能的內(nèi)存來提高日志記錄效率。例如程序在進行業(yè)務(wù)流程循環(huán)時,如果內(nèi)存足夠大,可以申請一塊內(nèi)存作為內(nèi)存日志記錄區(qū)。每次業(yè)務(wù)流程開始后,通過日志開關(guān)控制將當(dāng)前日志信息寫入內(nèi)存日志區(qū),等業(yè)務(wù)流程處理結(jié)束時,切換日志開關(guān),一次性將內(nèi)存日志記錄到硬盤中,形成硬盤上的日志文件。當(dāng)業(yè)務(wù)流程處理過程中發(fā)生中斷或者錯誤時,也通過控制參數(shù)調(diào)用日志記錄函數(shù)或方法,一次性將內(nèi)存日志記錄到硬盤中。日志函數(shù)的流程圖如圖5。
這樣處理的方式有幾個優(yōu)點:
1) 寫入內(nèi)存速度比硬盤速度高近百倍,因此整個日志記錄過程速度會更快。
2) 一次性寫入日志到日志文件,減少了頻繁打開關(guān)閉日志文件的次數(shù),提高了處理效率;
3)節(jié)約了硬盤空間,因為在業(yè)務(wù)運行正常的情況下只記錄關(guān)鍵信息,不必記錄過多細(xì)節(jié)。
4)在業(yè)務(wù)流程處理失敗的情況下保留了足夠多的用于故障定位的日志信息。
圖4 流水號日志的流程圖
這種處理方式的唯一風(fēng)險是系統(tǒng)突然掉電導(dǎo)致內(nèi)存日志區(qū)的數(shù)據(jù)丟失。折中的處理辦法是每次在業(yè)務(wù)循環(huán)處理中的關(guān)鍵節(jié)點處將內(nèi)存日志區(qū)的內(nèi)容寫入硬盤中,適當(dāng)犧牲小部分性能來換取可靠性。
日志的主要功能是記錄系統(tǒng)運行狀態(tài)以及為調(diào)測和故障定位提供必要信息,是軟件設(shè)計中需要考慮的重要部分。在進行日志功能設(shè)計時,除了常規(guī)的運行記錄需求外,還必須結(jié)合軟硬件環(huán)境和特定客戶需求,從內(nèi)容、格式、實現(xiàn)方法等多方面考慮,才能在后續(xù)的調(diào)測、聯(lián)調(diào)、正式運行中發(fā)揮功用。本文中提到的日志功能的設(shè)計要點及實現(xiàn)方式,在控制系統(tǒng)的軟件設(shè)計上是需要經(jīng)常加以關(guān)注的。
圖5 日志函數(shù)的流程圖
[1]www.w3.org. RFC3164. 2001年8月.
[2]謝美意 朱虹 馮玉才. 自修復(fù)數(shù)據(jù)庫系統(tǒng)日志機制研究. 計算機科學(xué) 2010年4月.
[3]王偉 楊永川. Windows Vista系統(tǒng)日志文件格式分析及數(shù)據(jù)恢復(fù). 計算機安全, 2009年4月.
[4]JIM TURNER. 確保成功地備份事件日志的一種方法. Windows IT Pro magazine, 2008年8月.
[5]李甜. 基于 Syslog的日志審計系統(tǒng)的研究和實現(xiàn).中國新通訊, 2008年9月.