1. 前言
服務端日誌你有多重視?
- 我們沒有日誌
- 有日誌,但基本不去控制需要輸出的內容
- 經常微調日誌,只輸出我們想看和有用的
- 經常監控日誌,一方面幫助日誌微調,一方面及早發現程序的問題
只做到第1點的,你可以洗洗去睡了。很多公司都有做到第2點和第3點,這些公司的服務端程序基本已經跑了很長時間了,已比較穩定,確實無需花太多時間去關注。如果一個新產品,在上線初期,我覺得就有必要做到第4點。
日誌怎麼看?
- 都說了,我們沒有日誌
- 線上日誌逐個tail+grep
- 編寫腳本,下載某個時間範圍內的全部日誌到本地再搜索
tail+grep或者把日誌下載下來再搜索,可以應付不多的主機和應用不多的部署場景。但對於多機多應用部署就不合適了。這裏的多機多應用指的是同一種應用被部署到幾臺服務器上,每臺服務器上又部署着不同的多個應用。可以想象,這種場景下,爲了監控或者搜索某段日誌,需要登陸多臺服務器,執行多個tail
-F
和grep
命令。一方面這很被動。另一方面,效率非常低,數次操作下來,程序員的心情也會變糟(我還要去維護宇宙和平的好嘛)。
這篇文章講的就是如何解決分佈式系統的日誌管理問題。先給大家看看最終的效果:
單個屏幕上所有服務器的日誌實時滾動着顯示。每條日誌開頭還標明日誌的來源(下圖)。
實現這種效果的原理是後臺跑着一個程序,這個程序負責彙總所有日誌到一個本地文件中。只要執行tail -f
這個文件就可以做到監控日誌了。因爲所有日誌都彙總在一個文件裏了,所以做日誌搜索的時候只要針對這一個文件搜索就可以了。
能夠彙總日誌文件的工具名字叫Logstash,即本文的介紹重點。它使用JRuby編寫,開源,主流,免費,使用簡單(宇宙和平使者必備單品)。
2. Logstash部署架構
Logstash的理念很簡單,它只做3件事情:
- Collect:數據輸入
- Enrich:數據加工,如過濾,改寫等
- Transport:數據輸出
別看它只做3件事,但通過組合輸入和輸出,可以變幻出多種架構實現多種需求。這裏只拋出用以解決日誌彙總需求的部署架構圖:
解釋術語:
- Shipper:日誌收集者。負責監控本地日誌文件的變化,及時把日誌文件的最新內容收集起來,輸出到Redis暫存。
- Indexer:日誌存儲者。負責從Redis接收日誌,寫入到本地文件。
- Broker:日誌Hub,用來連接多個Shipper和多個Indexer。
無論是Shipper還是Indexer,Logstash始終只做前面提到的3件事:
- Shipper從日誌文件讀取最新的行文本,經過處理(這裏我們會改寫部分元數據),輸出到Redis,
- Indexer從Redis讀取文本,經過處理(這裏我們會format文本),輸出到文件。
一個Logstash進程可以有多個輸入源,所以一個Logstash進程可以同時讀取一臺服務器上的多個日誌文件。Redis是Logstash官方推薦的Broker角色“人選”,支持訂閱發佈和隊列兩種數據傳輸模式,推薦使用。輸入輸出支持過濾,改寫。Logstash支持多種輸出源,可以配置多個輸出實現數據的多份複製,也可以輸出到Email,File,Tcp,或者作爲其它程序的輸入,又或者安裝插件實現和其他系統的對接,比如搜索引擎Elasticsearch。
總結:Logstash概念簡單,通過組合可以滿足多種需求。
3. Logstash的安裝,搭建和配置
3.1. 安裝Java
下載JDK壓縮包。
一般解壓到/user/local/
下,形成/usr/local/jdk1.7.0_79/bin
這種目錄結構。
配置JAVA_HOME
環境變量:echo
'export JAVA_HOME=/usr/local/jdk1.7.0_79' >> ~/.bashrc
。
3.2 安裝Logstash
去官網下載Logstash的壓縮包。
一般也解壓到/usr/local/
下,形成/usr/local/logstash-1.4.3/bin
這種目錄結構。
Logstash的運行方式爲主程序+配置文件。Collect,Enrich和Transport的行爲在配置文件中定義。配置文件的格式有點像json,又有點像php。
3.3. 編寫Shipper角色的配置文件:shipper.conf
1
2
3
4
5
6
7
8
9
|
input
{ file
{ path
=> [ #
這裏填寫需要監控的文件 "/data/log/php/php_fetal.log" , "/data/log/service1/access.log" ] } } |
如上,input描述的就是數據如何輸入。這裏填寫你需要收集的本機日誌文件路徑。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
output
{ #
輸出到控制檯 #
stdout { } #
輸出到redis redis
{ host
=> "10.140.45.190" #
redis主機地址 port
=> 6379 #
redis端口號 db
=> 8 #
redis數據庫編號 data_type
=> "channel" #
使用發佈/訂閱模式 key
=> "logstash_list_0" #
發佈通道名稱 } } |
如上,output描述的就是數據如何輸出。這裏描述的是輸出到Redis。
data_type的可選值有channel
和list
兩種。用過Redis的人知道,channel是Redis的發佈/訂閱通信模式,而list是Redis的隊列數據結構。兩者都可以用來實現系統間有序的消息異步通信。channel相比list的好處是,解除了發佈者和訂閱者之間的耦合。舉個例子,一個Indexer在持續讀取Redis中的記錄,現在想加入第二個Indexer,如果使用list
,就會出現上一條記錄被第一個Indexer取走,而下一條記錄被第二個Indexer取走的情況,兩個Indexer之間產生了競爭,導致任何一方都沒有讀到完整的日誌。channel
就可以避免這種情況。這裏Shipper角色的配置文件和下面將要提到的Indexer角色的配置文件中都使用了channel
。
1
2
3
4
5
6
|
filter
{ mutate
{ #
替換元數據host的值 replace
=> [ "host" , "10.140.46.134
B[1]" ] } } |
如上,filter描述的是如何過濾數據。mutate是一個自帶的過濾插件,它支持replace操作,可以改寫數據。這裏改寫了元數據中的host字段,替換成了我們自己定義的文本。
Logstash傳遞的每條數據都帶有元數據,如@version,@timestamp,host等等。有些可以修改,有些不允許修改。host記錄的是當前主機的信息。Logstash可能不會去獲取主機的信息或者獲取的不準確,這裏建議替換成自己定義的主機標示,以保證最終的日誌輸出可以有完美的格式和良好的可讀性。
3.4 編寫Indexer角色的配置文件:indexer.conf
1
2
3
4
5
6
7
8
9
|
input
{ redis
{ host
=> "10.140.45.190" #
redis主機地址 port
=> 6379 #
redis端口號 db
=> 8 #
redis數據庫編號 data_type
=> "channel" #
使用發佈/訂閱模式 key
=> "logstash_list_0" #
發佈通道名稱 } } |
如上,input部分設置爲從redis接收數據。
1
2
3
4
5
6
7
|
output
{ file
{ path
=> "/data/log/logstash/all.log" #
指定寫入文件路徑 message_format
=> "%{host}
%{message}"
# 指定寫入格式 flush_interval
=> 0 #
指定刷新間隔, 0 代表實時寫入 } } |
如上,output部分設置爲寫入本地文件。
官方文檔裏flush_interval爲緩衝時間(單位秒)。我實踐下來不是秒而是數量,Logstash會等待緩衝區寫滿一定數量後才輸出。這對線上調試是不能接受的,建議上線初期設爲0。程序穩定後,隨着日誌量的增大,可以增大flush_interval的值以提高文件寫入性能。
Indexer的配置文件中,我明確指定了message_format的格式,其中%{host}
對應的就是之前手動設置的host元數據。
3.5. 啓動Logstash
1
2
3
4
5
6
|
#
先在Indexer主機上啓動 nohup
/usr/local/logstash- 1.4 . 3 /bin/logstash
agent -f indexer.conf &>/dev/ null & #
然後在Shipper主機上啓動 nohup
/usr/local/logstash- 1.4 . 3 /bin/logstash
agent -f shipper.conf &>/dev/ null & #
最後在Indexer上觀察日誌 tail
-f /data/log/logstash/all.log |
我們來測試一下,切到Shipper主機上,模擬日誌產生:
1
|
echo "Hello
World"
>> /data/log/php/php_fetal.log |
再切換到Indexer主機上,如果出現:10.140.46.134 B[1] Hello World
,說明Logstash部署成功。
3.6. 日誌着色腳本
在tail -f
的時候,如果使用awk
配合echo
,可以匹配你想要高亮的文本,改變他們的前景色和背景色。就像效果圖裏的那樣(這是宇宙和平使者必備單品的重要屬性好嘛)。這裏附上我寫的腳本,把腳本中的關鍵信息替換成你想要匹配的文本即可:
1
2
3
4
5
|
tail
-f /data/log/logstash/all.log | awk '{ if (match($ 0 ,
/.*(PHP Deprecated|PHP Notice|PHP Fatal error|PHP Warning|ERROR|WARN).*/)) { print "\033[41;37;1m" $ 0 "\033[0m" }
else if
(match($ 0 ,
/.*關鍵信息 1 .*/))
{ print "\033[32;1m" $ 0 "\033[0m" }
else if
(match($ 0 ,
/.*關鍵信息 2 .*/))
{ print "\033[36;1m" $ 0 "\033[0m" } else {
print $ 0 }
}' |
So easy,媽媽再也不用擔心我的日誌。。。
4. 還有什麼
有些公司需要挖掘日誌的價值,那僅僅收集和實時顯示是不夠的,需要把逼格上升到日誌分析技術層面。
一個完整的日誌分析技術棧需要實時收集,實時索引和展示三部分組成,Logstash只是這其中的第一個環節。Logstash所屬的Elastic公司,已經開發了完整的日誌分析技術棧,它們是Elasticsearch,Logstash,和Kibana,簡稱ELK。Elasticsearch是搜索引擎,而Kibana是Web展示界面。
如果你搭建了完整的技術棧,你的老闆就可以在圖形化界面上按不同的維度去搜索日誌了。
還可以做一些高大上的統計和計算。
當然,我認爲90%的公司是沒有必要這麼做的(畢竟他們的程序員還要去維護宇宙的和平),能做到在控制檯裏監控和搜索就能滿足需要了。但我們也可以看看剩下的那10%的公司是怎麼做的,比如這篇文章:新浪是如何分析處理32億條實時日誌的?