filebeat 是基于libbeat 来build 的一个开源库, filebeat 总共分5个部分:
1. input 在给定的路径上寻找文件,并启动扫割进程来收割文件的数据
2. harvester: 收割机, 读一个文件,然后发送一个事件给spooler
3. spooler: 收集harvester 的发送过来的事件,等待一定时间将事件flush 给publisher
4. publisher: 写数据到network, 然后通知registrar
5. registrar: 记录文件数据读取的位置
从上面的架构可以看出一个prospector(input) 可以scan 多中log, 每一个log file 对应了一个Harvester.
接下来我们从走读source code 来看看filebeat 的启动, filebeat src 下面有一个main.go 是filebeat 的入口, main 的入口处正如跟程序一样, 刚开始去解析cli 的参数, filebeat 把这块code封装在cmd 的package 中, filebeat 中将beat 的实现放在了filebeat/beater.go 将实例带入 libbeat/cmd package 中, cmd 中的实例实现在cmd/instance 中的beat.go, instance/beat.go 中分别实现了BeatConfig, Setup, Run 等等。
我们来看看Run 的code:
func Run(name, idxPrefix, version string, bt beat.Creator) error {
return handleError(func() error {
defer func() {
if r := recover(); r != nil {
logp.NewLogger(name).Fatalw("Failed due to panic.",
"panic", r, zap.Stack("stack"))
}
}()
b, err := NewBeat(name, idxPrefix, version)
if err != nil {
return err
}
return b.launch(bt)
}())
}
首先我们会根据name 来create 一个Beat 对于filebeat 来说name 自然就是filebeat 了. 这里主要通过launch 来launch 一个beat 的. launch 的参数其实是一个函数,这个函数就是filebeat/cmd 那边带来的(也就是filebeat 的中的New 函数)
launch 中有三步非常重要:
a. Init Beat, 这个将会去Initialize plugin 和解析configuration
b. 利用filebeat 的New 函数作为参数来create 一个beater, createBeater 将返回一个beater , 并且会根据configuration 去加载pipeline (也就是相应的publisher, ElasticSearch/Logstash/redis/console 等等)
c. 将b 中create 的beater Run 起来.
重点我们来看b 和c
pipeline 的Load 主要在publisher 的pipeline package 中, 下面是load 的一段主要code:
queueBuilder, err := createQueueBuilder(config.Queue)
out, err := loadOutput(beatInfo, reg, outcfg)
p, err := New(beatInfo, reg, queueBuilder, out, settings)
从上面可以知道,我们create output 主要有三步,create queue, loadOutput, 然后根据queue 和out 创建pipeline 返回去。
然后我们接下去看一下loadOutput 这个函数, 它主要call
out, err := outputs.Load(beatInfo, outStats, outcfg.Name(), outcfg.Config())
这个根据beat 的信息,out observer 还有out put name 以及out put config 来Load 一个out. outputs 中包括多个output plugin
这只是粗线条的说明了pipeline 加载的code. 接下来我们来看beater Run 的code
libbeat 中的Launch 中最后一步就是beater的run, 对于Filebeat 来说当然就是 filebeat/beater 的Run 函数了, 这个函数中最主要的两步是
registrar 的create 和start
crawler 的 create 和start
我们主要看看crawler 了, 这个在架构中没有体现出来,在code 层面上这个是相当重要的, 对于filebeat 来说.crawler负责具体的日志采集工作。它会根据配置文件启动多个prospector,每个prospector处理一类日志文件类型,有着一组独立的配置。prospector会启动一个prospectorer干主要的活。根据默认配置,这个prospectorer会是ProspectorLog类型的。ProspectorLog类型的prospectorer会扫描目标路径下匹配的文件,根据registry里存储的状态判断每个文件。如果之前处理过,调用harvestExisingFile;否则调用harvestNewFile。前者会判断对应的harvester是否还在运行。这两个函数都涉及到一个Harvester的创建。随便一提,registry是registrar创建之后传递给crawler的,里面是文件的处理状态记录.
对于ProspectorLog来说,它在创建Harvester时调用的是harvester/log.go中的Harvest方法。该方法首先创建一个LineReader, 我们来看看调用的关系了
现在我们随着FileEvent(以下简称为事件)来到spooler里面。一进来,就看到事件们在这里排起了长龙。spooler接到事件后,不急着发出去,而是排进队列中。如果队列(长度取决于spool_size,默认2048)已满,调用flush方法把事件刷到publisher里面。此外配置的flush时间idle_timeout(默认5s)到时后,也会调用flush方法。publisher 将数据会送到一个叫registry, registry 会将数据做持久化并更新进度。
那publisher 又是怎么工作的?其实publisher 中有一个Client, 这个Client由libbeat/publisher/publish.go的Connect函数创建的, 我们可以以ElasticSearch 为例看看他的的代码结构:
以上我只是粗略的分析了一下filebeat 的source code, 具体的还要慢慢琢磨。