第零章 綜述
最近因為工作需要研究了rsyslog,主要是把官網的文檔看了一遍,因為學習的過程中,發現中文資料很少,所以研究的差不多以后,決定拿出來分享一下。
rsyslog官網的文檔還是挺爛的(在我看完后,官網有了一次改版,可能好一些了),尤其是配置文件部分,每個版本都有改動,但是官網只有最新版本的參數介紹,好幾次我按照文檔的參數寫配置文件,或者報錯,或者參數不起作用,因此大家使用哪個版本的rsyslog,最好是下個該版本的源代碼,以供核查參數是否准確。
本文的結構,來自我的內部分享。
使用的rsyslog版本為:7.2.5
第一章 rsyslog整體架構
我主要使用rsyslog來保證日志的可靠性傳輸,日志中心宕機,client端可以本地保存日志,因此rsyslog與數據庫結合方面的知識在文章中沒有涉及。
Rsyslog架構,這是rsyslog官網上的一張圖,用來介紹rsyslog的架構,rsyslog的消息流是從輸入模塊->預處理模塊->主隊列->過濾模塊->執行隊列->輸出模塊。
在這個流程圖中,輸入、輸出、過濾三個部分稱為module,輸入模塊有imklg、imsock、imfile。輸出模塊有omudp、omtcp、omfile、omprog、ommysql、omruleset(后兩者我沒有研究,本文不會涉及)。過濾模塊研究不多,只會提到mmnormalize。
預處理模塊主要解決各種syslog協議實現間的差異,舉例說明如果日志系統client端使用rsyslog、server端使用syslog-ng,如果自己不做特殊處理syslog-ng是無法識別的。但是反過來,rsyslog的server端就可以識別syslog-ng發過來的消息。
Input模塊包括imklg、imsock、imfile、imtcp等,是消息來源。
Filetr模塊處理消息的分析和過濾,rsyslog可以根據消息的任何部分進行過濾,后面會介紹到具體的做法。
Output模塊包括omfile、omprog、omtcp、ommysql等。是消息的目的地。
Queue模塊負責消息的存儲,從Input傳入的未經過濾的消息放在主隊列中,過濾后的消息放入到不同action queue中,再由action queue送到各個輸出模塊。
1.1啟動參數
正常啟動:指定-f和-i就可以了,新版本不需要-c 5 這樣的參數。
rsyslogd -f /root/rsyslog_worker_dir/rsyslog.conf -i /root/rsyslog_worker_dir/rsyslog.pid
debug版本:debug消息會輸出到標注輸出,如果出現未預期的結果,可以嘗試使用debug方式,查看處理流程
rsyslogd -f /root/rsyslog_worker_dir/rsyslog.conf -i /root/rsyslog_worker_dir/rsyslog.pid -dn >debuglog
測試配置文件是否正確:
rsyslogd -N1 -f file
第二章 rsyslog的概念
2.1屬性替代
Rsyslog 預定義了一些屬性,來代表消息中的信息,我們可以在定義輸出格式、動態文件名的時候使用到這些屬性。這里面比較重要的屬性比如:msg(消息體)、hostname、pri(消息等級和類別)、time(時間有關),屬性的名稱中以$開頭的是從本地系統獲得的變量、不帶$是從消息中獲得變量。
屬性替代的語法格式:
%propname:fromChar:toChar:options:fieldname%
屬性替換的功能很強大,你可以使用起始字符獲取自己所需的字段,也可以使用正則表達式,也可以使用分隔符。舉幾個例子:為了兼容一個rfc協議,rsyslog規定如果用戶輸入的msg不是以空格開頭,rsyslog會自動補充一個空格,因此如果你希望輸出的時候去掉這個空格,就可以使用
%msg:2:$% #選取msg變量中,起始位置為2,終止位置為結尾
我們經常需要根據空格來分析字符串,F表示使用字符分割,32是空格的ascii碼,例:
%msg:F,32:3% #按照空格分隔,取第三個子串,
正則匹配可以匹配特定的文字和格式,我的正則比較差, 避免了使用這部分的內容,所以這部分沒有例子了。
屬性替代中還用到了一類特殊的以$!開頭的變量,這是使用mmnormalize模塊時特有的,可以實現類似於syslog-ng中parser模塊的功能。后面再講
2.2模板
模板的功能是定義輸出格式,或者定義omfile模塊的動態路徑、動態文件。需要使用上面提到的屬性替換。
模板定義的形式有四種,適用於不同的輸出模塊,一般簡單的格式,可以使用string的形式,復雜的格式,建議使用list的形式,使用list的形式,可以使用一些額外的屬性字段(property statement),例如:position.from、position.end。
如果不指定輸出模板,rsyslog會默認使用RSYSLOG_DEFAULT。
如果你只想輸出msg,可以定義模板
$template t_msg, “%msg\n%”
如果想按日期保存輸出,需要使用動態路徑。可以定義模板
$template f_debug, “/data0/logs/%$year%-%$month%-%$day%/debug.log”
2.3Ruleset
Ruleset實現的是多實例的功能,可以針對syslog的來源使用不同的過濾規則。需要注意的是,在配置文件中需要先定義ruleset,才可以使用。比較典型的一個例子,針對不同的端口使用不同的過濾規則。
$Ruleset tcp1999 $RulesetCreateMainQueue on Local3.* @@10.0.0.44:1999 $Ruleset tcp2000 $RulesetCreateMainQueue on Local4.* @@10.0.0.44:2000
在定義好ruleset后,各個輸出模塊就可以指定自己使用的ruleset了,具體如何指定,可以查看輸出模塊的手冊,一般會有一個ruleset的參數,用來實現這個功能。
2.4Filter模塊
Rsyslog可以使用syslog標准的過濾規則,同時自己添加了一些擴展。比如可以在輸出中指定rsyslog自己的處理方式,可以指定輸出template,方法是在規則后面添加template的名字,用分號隔開。
例如我們可以編寫一個規則
Local3.* -/data0/logs/local3.log;t_msg #在這個輸出中使用t_msg的模板
Local4.* -?f_local3_test;t_msg #問號表示要使用模板定義的動態路徑
除了syslog標准的規則,rsyslog的作者還自己開發了一個叫做rainerscript的腳本語言,來定義更復雜的過濾過則,rainerscript可以對屬性進行startwith、contains、%(取余)等過濾規則,例如
If $pri-txt == local3.* and $msg contains “abc” then{ #pri為local3,且在消息中包含子串‘abc’ *.* -/data0/logs/local3.log;t_msg }
還有第三種方式是使用屬性的表示方式,例如
:msg, regex, "^ [g-z]" /root/rsyslog_worker_dir/2000.log #以字母g到z開頭的消息,注意msg開頭有個空格
2.5隊列
隊列是rsyslog中比較重要的一個部分,作為使用者,我們需要了解的是隊列的種類:主隊列和工作隊列。從輸入模塊接收的消息會進入主隊列,主隊列中的消息,經過過濾模塊,會進入到相應的工作隊列;隊列的四種工作模式:direct mode、disk mode、FixedArray mode和LinkedList mode,前兩種是磁盤隊列,更可靠,但是性能也較差,后兩種是內存隊列,區別是前者是預分配隊列長度,后者是動態分配,如果你的系統日志流量比較平穩,可以使用預分配隊列,如果日志屬於突發型,可以使用動態隊列。此外,內存隊列還可以通過指定一個queuename來添加DA模式,DA模式主要是為了防止意外情況(進程關閉、server端宕機)下,內存隊列可以不丟失。
通過查看rsyslog的系統命令,可以知道rsyslog對隊列進行大量的可配參數,來定義隊列的行為。可以根據需要來進行優化。
第三章 實例
3.1 Tcp+DA模式實現可靠消息傳輸。
Rsyslog單獨使用了一篇文檔來介紹實現可靠消息傳輸。
首先rsyslog闡述了單獨使用tcp協議的不可靠性,比如server端宕機等等情況。為此如上面介紹隊列時提到的內容,我們需要在client配置一個本地文件,用來在server端宕機這種情況下,暫時保存消息。需要注意的是,隊列名是和過濾規則對應的,一個隊列只能用於一個過濾規則,例:
$ActionQueueType LinkedList $ActionQueueFileName local3 $ActionResumeRetryCount -1 $ActionQueueSaveOnShutdown on Local3.* @@10.0.0.44:1999 $ActionQueueType LinkedList $ActionQueueFileName local4 $ActionResumeRetryCount -1 $ActionQueueSaveOnShutdown on Local4.* @@10.0.0.44:1999
此處只是為了說明問題,更簡單的寫法,是把兩條過濾規則寫成一條:local3.*;local4.*。還有一點需要強調的是,本地隊列只有在需要使用的時候才會創建,當后端出現短暫不可用是,rsyslog的內存隊列就可以保存消息,內存隊列不夠用時,才會創建本地隊列。
3.3 mmnormalize模塊
當我們需要定義一個復雜的輸出時,單純使用模板會顯得比較笨拙,這時候我們就可以使用mmnormalize這個模塊來幫助我們簡化模板。例如我們部門本身使用一套日志格式,但是某個重要客戶,需要將他自己的日志按照他所規定的格式保留一份,為此,我們需要定義一個過濾規則,過濾該用戶的日志,然后打散日志,重排,下面我會用模板和mmnormalize兩種方式來解決這個問題。
首先使用模板,我使用list的方式定義:
template(name="t_weiyouxi_rewrite" type="list"){ property(name="msg" field.Delimiter="32" field.Number="2") constant(value=" ") property(name="msg" field.delimiter="32" field.number="3") constant(value=" ") property(name="msg" field.delimiter="32" field.number="4") constant(value="us - ") property(name="msg" field.delimiter="32" field.number="6") constant(value=" ") property(name="msg" field.delimiter="32" field.number="7") constant(value=" \"") property(name="msg" field.delimiter="34" field.number="2") constant(value="\"") property(name="msg" field.delimiter="34" field.number="3") constant(value="\"") property(name="msg" field.delimiter="34" field.number="4") constant(value="\" - \"UTRS1=") property(name="msg" field.delimiter="34" field.number="7" position.from="2") constant(value=";U_TRS2=-;SUP=-\" \"") property(name="msg" field.delimiter="34" field.number="6") constant(value="\" ") property(name="hostname") constant(value="\n") }
這種方式寫出的模板是很冗長的,下面使用mmnormalize模塊實現,為此,我們需要先定義一個消息格式,來供mmnormalize使用,rulebase.rb:
rule=: %xxxdomain:word% %xxx-ip:ipv4% %xxx-resptime:number% %xxx-cputime:number% [%xxx-timestamp:char-to:]%] malibumytime 819 %xxx-version:number% "%xxx-method:word% %xxx-url:word% %xxx-protocol:char-to:"%" %xxx-retcode:number% %xxx-retsize:number% "%xxx-referer:char-to:"%" "%xxx-ua:char-to:"%" %xxx-cookie:word%
然后,rsyslog的配置文件中,我們可以將以上定義的字段當做屬性來使用(需要以$!開頭),如下:
$mmnormalizeRuleBase /path/to/rulebase.rb $template t_weiyouxi_rewrite, "%$!xxxdomain% %$!xxx-ip% %$!xxx-resptime%us - [%$!xxx-timestamp%] \"%$!xxx-method% %$!xxx-url% %$!xxx-protocol%\" %$!xxx-retcode% %$!xxx-retsize% \"%$!xxx-referer%\" - \"UTRS1=%$!xxx-cookie%;U_TRS2=-;SUP=-\" \"%$!xxx-ua%\" %hostname%\n"
可以看出這種方式比使用模板要簡潔清楚多了。不過額外的你需要多使用一個配置文件。
3.4 omprog模塊
如文檔所表述的一樣,omprog可以指定第三方的程序來處理模塊,運行時,第三方的模塊被當做rsyslog的子進程啟動,兩者通過管道通信。此時過濾規則定義的模板,就是子進程的輸入格式。
$ActionOMProgBinary /root/rsyslog_worker_dir/prog/start.sh #腳本可以保證第三方程序可以使用自己的啟動參數 local3.* :omprog:;t_msg
3.5額外的測試。
1、測試了imtcp和imptcp的區別,測試了兩者的性能差不多,ptcp略低,這和官網說的ptcp性能更好不一致,可能我還沒有壓到極限,沒有測出真實數據,測試過程中imtcp的cpu使用較高,大概在130%,imptcp的cpu大概在70%左右,可以預見ptcp使用多線程技術,分擔了主線程的壓力。
2、其他部門的同事測試tcp+da可以解決server端宕機的問題,但是如果server端因為io較高,造成阻塞,rsyslog並不能解決這個問題,syslog阻塞是個很嚴重的問題,如果apache直接寫syslog,而syslog阻塞會導致apache無法處理請求。因此必須在server端加監控,如果io較高,就需要優化參數,或者增加server機器了。
3、使用-dn模式來測試mmnormalize模塊,如過mmnormalize沒有收到正確的rulebase,debug文件中會搜索‘unparser’會看到未解析的rulebase字符串,如果解析成功,可以搜索‘gennerate’,可以看到自定義的屬性,在真實消息中的值。
4、rsyslog中的參數有的是全局參數,例如Createmode,有的則是對應於某一ruleset或者某一過濾規則,所以使用前,需要對參數的有效范圍進行測試。
5、如果你希望client和server端都使用rsyslog,那么你不要修改兩者之間的傳輸模板。因為rsyslog 需要根據syslog協議來解析消息,如果你使用t_msg傳輸,那么server端無法識別該消息。
6、同事反饋之前使用syslog-ng時消息超過設置的最大消息長度,會分成兩條發送,rsyslog行為不同,超過設置的最大消息長度,超出部分會丟棄。
7、rsyslog默認會將特殊字符(\t)轉換成#009 由全局配置$EscapeControlCharactersOnReceive 決定,如果自己需要根據\t處理輸出時,需將該選項改為off。
8、配置文件每一行之前,只能包含空格,如果半酣其他字符,rsyslog不能識別該行。
9、使用omprog時,rsyslog通過管道將消息發送給第三方程序, 因此當消息量較大時,第三方程序要具備相應的處理能力,否則很容易造成管道阻塞,導致rsyslog阻塞。這個問題挺嚴重的,我們的經驗是server日志轉發到redis,但是開始的程序處理能力達不到,導致server端的tcp接收隊列阻塞。
3.6 未解決的問題
1、本想測試template和mmlognorm兩種方式那種更快,但是發現ab壓的時候,總是會多出一些輸出日志,不知道是不是壓力太大,導致有retry操作,發生概率大概3%,現有測試數據,兩者的性能差不多。
2、使用 -dn的debug模式,輸出太多,不使用debug模式的輸出又太少,希望找到其他的輸出參數,失敗了,估計要看代碼了。