敲代碼很是難之logstash之file input插件實現分析

版權聲明:本文爲橫雲斷嶺原創文章,未經博主贊成不得轉載。微信公衆號:橫雲斷嶺的專欄 https://blog.csdn.net/hengyunabc/article/details/25665877

前言

敲代碼有時候真的有點難,要考慮各類狀況。css

應用在執行中,會不斷生成日誌文件。html

假如要實現一個日誌收集的工具。不考慮其餘的分析功能,僅僅考慮收集,有哪些方面要考慮的?node

首先看下通常的log框架是怎樣輸出日誌的:mysql

多是這種:a.log.1,  a.log.2, a.log.3, a.log.4, a.log.5 循環輸出。linux

多是這種: a.2014-5-5.log, a.2014-5-6.log, a.2014-5-7.log,天天生成一個日誌文件;git

多是這種:log.out。每次從新啓動都會生成一個新的log.out,覆蓋舊的文件。github


那麼,咱們有哪些方面要實現和注意的?redis

  • 提供正則或者globs方式的通配符。
  • 要能推斷文件是否是新建的。
  • 怎樣推斷文件有沒有更新?
  • 怎樣保存文件的讀取進度?
  • 假設咱們在讀取文件的過程當中,文件被刪除了會怎樣?
  • 假設咱們在讀取文件過程當中,進程掛了,讀取進度有沒有及時保存?
  • 在保存文件進度時,假設掛了,從新啓動能不能正確恢復文件進度?
  • 能不能保證讀取的內容不反覆?
  • 假設日誌文件很是快生成。又很是快刪除了,可否保證不漏掉?
  • 假設日誌文件是軟連接(soft link)。能不能正確處理?
  • 文件系統的inode會被回收利用,能不能處理這個?
  • 有沒有控制讀進內存的數據的大小,防止佔用過多的內存?

logstash的實現

如下解釋下logstash是怎樣實現和處理上面的問題的:sql

可以配置path參數(Array),當中支持globs風格的匹配。如:ruby

path => [ "/var/log/messages", "/var/log/*.log" ]

可以配置exclude參數(Array),排除掉不需要的文件。如:

exclude => "*.gz"

利用inode來識別新文件

logstash把進度保存到所謂的sincedb裏,實際上即這種一個文本文件,默認是放在home文件夾下的。如:

.sincedb_e794081d6134aace51b759aea8cc3be2

.sincedb_f7a0c8a0def03e0c572511ceea0b9f63

後面是日誌文件。即path的hash值。

這樣就區分了不一樣的文件名稱的日誌文件的進度保存問題。

sincedb文件中是類似這種內容:

6511055 0 2051 118617881
5495516 0 2051 155859036
6348913 0 2051 148511449

上面的4列各自是:

inode, major number, minor number, pos。

當中major number和minor number是設備相關的數字,參考:http://unix.stackexchange.com/questions/73988/linux-major-and-minor-device-numbers

inode是文件系統給文件分配的是一個號碼,參考:http://zh.wikipedia.org/wiki/Inode

所以logstash區分了設備。文件名稱,文件的不一樣版本號。

這裏引出了一個新問題。用inode來推斷文件的不一樣版本號,是否夠準確了?因爲inode是會回收再使用的。

比方依次執行如下的命令,可以發現。兩個文件的inode是同樣的:

touch test
stat test
rm test 
touch test
stat test

但是因爲logstash是沒有close掉文件,因此是一直持有inode,因此新的同名的日誌文件會有一個新的inode。

也正是因爲這樣,假設logstash監視的日誌文件假設被刪除了,仍是可以繼續把刪除的文件的內容處理完。

利用inode這點特性,有時可以作一些補救工做,比方不當心把mysql的文件刪掉了,仍是可以把數據dump出來,因爲mysql進程還持有數據文件的inode。

另外,logstash默認是每隔1秒就嘗試讀取文件有沒有新內容,默認是15秒就掃描,檢查有沒有新文件。相應stat_interval和discover_interval參數。


另外一些小細節:

比方每次最多僅僅讀取出16394字節的數據,防止佔用過多的內存,每5秒推斷下是否需要保存新的pos。

假設日誌文件被刪除了,也會刪除sincedb文件。

利用rename原子性地保存pos

當讀取到新文件內容時。pos會添加,在保存新的pos到sincedb時,logstash採用了暫時文件的辦法:

先創建一個暫時文件,寫入新內容。再調用操做系統提供的remane函數。原子性地替換原來的sincedb文件。

這種其實是比較常用的技巧了,redis也是這樣子作的。

可否保證不反覆,不丟失數據?

很是遺憾。這是不能的,除非是分佈式事務。不然,總有可能丟失或者反覆發送數據。不論什麼日誌收集軟件或者消息隊列軟件都是如此。

實現的代碼

詳細的實現代碼就不貼了,因爲比較易讀,當中logstash使用了filewatch這個庫。可以用gem來安裝。相關的代碼在線查看:

https://github.com/elasticsearch/logstash/blob/v1.4.1/lib/logstash/inputs/file.rb

https://github.com/jordansissel/ruby-filewatch/tree/master/lib/filewatch

和fluentd的in_tail插件比較

fluentd也是一個很是流行的日誌收集工具。

簡單再看了下fluentd的in_tail插件,發現裏面還有本身當年提交的一個防止內存佔用過大的建議:)

https://github.com/fluent/fluentd/blob/master/lib/fluent/plugin/in_tail.rb

 
  
                 if lines . size >= MAX_LINES_AT_ONCE
                   # not to use too much memory in case the file is very large
                   read_more = true
即每最多讀取1000行,就提交數據。並保存pos。

fluentd的in_tail插件的原理和logstash的file input是幾乎相同的,都是用inode來區分文件是否更新。


但是fluentd僅僅保存了inode和pos。沒有logstash那樣把設備都考慮進去了。

另外fluentd保存pos時,都是以文件追加的方式來保存的,沒有像logstash那樣是用rename文件來保存到新文件中。顯然logstash的實現更加合理。

扯遠一點。logstash部署要比fluentd方便。雖然二者都是用ruby寫的。不一樣的是logstash默認是jruby,僅僅要有JVM就可以跑,fluentd則要安裝ruby環境。比較麻煩。

其餘的一些東東:

logstash大有一統江湖之勢,這句話忘記在哪裏看到的了。在github上的logstash的start有2000多個。

logstash + elasticsearch + Kibana的日誌收集。搜索,展示的一條龍服務很是流行。

參考:

http://unix.stackexchange.com/questions/73988/linux-major-and-minor-device-numbers

http://zh.wikipedia.org/wiki/Inode

https://github.com/elasticsearch/logstash/blob/v1.4.1/lib/logstash/inputs/file.rb

相關文章
相關標籤/搜索