filebeat新filestream类型是否支持tail_files类似功能探究
背景
试水搭建ELK,使用了ELK7.17.13版本,filebeat默认配置的input type已经是filestream而非旧版的log类型,开始了探索之旅。
信任ChatGPT导致的三次失败尝试
ChatGPT3.5介绍说filestream是旧版log类型的替代者,提供了更多的功能和改进,该类型对于新加入文件默认是从尾部开始同步,简单测试后直接上了一台线上机器,log同步QPS破万,马上把ES机器 CPU跑满,妥妥地从文件头部开始读取同步,首次失败,。
ChatGPT继续查询说可以tail_files参数控制log和filestream类型加入新文件时是否从尾部读取的行为,出于谨慎加入tail_files: true参数后尝试同步测试环境的api log,发现依然是从头部开始读取,参数未生效,二次失败。
继续咨询ChatGPT tail_files不生效的可能原因,表示如果之前已经读取过了文件,即便后续修改了tail_files从尾部读取也不会生效,需要删除状态文件registry/filebeat/data.json,最终找到了7.17中对应的log.json文件进行删除,而后重启filebeat发现依然是从头读取整个文件,三次失败。
所谓尽信大模型则不如无大模型确实是有道理的,最终停掉filebeat同步后开始自主探究。
自行探究filestream的尝试
filestream的ignore_inactive参数
查询资料后发现filestream其实并没有tail_files这一参数,只有旧版的log类型才支持tail_files,有不少关于filestream有没有类似log类型的tail_files功能的提问,回答基本都是提到ignore_inactive这个参数,说是可以达到从尾部读取的功能,官方文档介绍如下:
ignore_inactive
If this option is enabled, Filebeat ignores every file that has not been updated since the selected time. Possible options are since_first_start and since_last_start. The first option ignores every file that has not been updated since the first start of Filebeat. It is useful when the Beat might be restarted due to configuration changes or a failure. The second option tells the Beat to read from files that have been updated since its start.
The files affected by this setting fall into two categories:
Files that were never harvested
Files that were harvested but weren’t updated since ignore_inactive.
For files that were never seen before, the offset state is set to the end of the file. If a state already exist, the offset is not changed. In case a file is updated again later, reading continues at the set offset position.
The setting relies on the modification time of the file to determine if a file is ignored. If the modification time of the file is not updated when lines are written to a file (which can happen on Windows), the setting may cause Filebeat to ignore files even though content was added at a later time.
To remove the state of previously harvested files from the registry file, use the clean_inactive configuration option.
ignore_inactive有两个选项since_first_start、since_last_start,分别代表filebeat首次启动时间和本次启动时间,filebeat对于首次启动或者本次启动时间之后的文件将直接ignore,即不同步其内容。
重点是后面的
For files that were never seen before, the offset state is set to the end of the file.
这句话直接可以理解为对于filebeat之前没有记录过的文件,offset将被直接置为文件尾部,于是看上去似乎将ignore_inactive设置为since_last_start就能达到tail_files: true的效果。
设置ignore_inactive: since_last_start后一例成功一例失败
在测试环境尝试同步一个微服务的日志文件,验证发现filebeat确实不再从头部读取整个文件内容同步,直到文件尾部有新的内容写入后,尾部的新内容才被filebeat同步最终写入的ES。
再次加入测试环境api主服务的日志文件同步,结果一模一样的配置api的日志文件却是从头开始同步的!!
ignore_inactive源码实现探究
尝试通过源码探究这看似古怪的现象,才算最终捋清了ignore_inactive这一参数的具体作用:
// beats/filebeat/input/filestream/prospector.go
167 func (p *fileProspector) onFSEvent(
168 log *logp.Logger,
169 ctx input.Context,
170 event loginp.FSEvent,
171 src loginp.Source,
172 updater loginp.StateMetadataUpdater,
173 group loginp.HarvesterGroup,
174 ignoreSince time.Time,
175 ) {
176 switch event.Op {
...
190 if p.isFileIgnored(log, event, ignoreSince) {
191 err := updater.ResetCursor(src, state{Offset: event.Descriptor.Info.Size()})
192 if err != nil {
193 log.Errorf("setting cursor for ignored file: %v", err)
194 }
195 return
196 }
...
如上可以看到onFSEvent函数在190行调用了isFileIgnored函数判定文件是否符合ignore条件,若符合则直接将其OffSet置为文件实际Size,亦即设置为从文件尾部开始读取,而isFileIgnored函数实现如下:
224 func (p *fileProspector) isFileIgnored(log *logp.Logger, fe loginp.FSEvent, ignoreInactiveSince time.Time) bool {
225 if p.ignoreOlder > 0 {
226 now := time.Now()
227 if now.Sub(fe.Descriptor.Info.ModTime()) > p.ignoreOlder {
228 log.Debugf("Ignore file because ignore_older reached. File %s", fe.NewPath)
229 return true
230 }
231 }
232 if !ignoreInactiveSince.IsZero() && fe.Descriptor.Info.ModTime().Sub(ignoreInactiveSince) <= 0 {
233 log.Debugf("Ignore file because ignore_since.* reached time %v. File %s", p.ignoreInactiveSince, fe.NewPath)
234 return true
235 }
236 return false
237 }
可以看到若ignoreInactiveSince有值--since_first_start/since_last_start,且当前时间>=ignoreInactiveSince则返回true,于是调用方onFsEvent将读取offset设置为文件尾,至于ignoreOlder对应于filestream的配置参数ignore_older,其判定机制类似,区别在于配置时是采用5m、5h这样的相对时间格式。
测试环境用于测试的微服务日志更新很少,可能几分钟才有新内容,所以filebeat重启后其更新时间小于since_last_start,也就被isFileIgnored判定为true,于是从尾部开始读取文件;而测试环境主api服务的log则是秒级更新,其会被isFileIgnored判定为false,所以会从头读取文件。
也就是说ignore_inactive能够对于新出现的文件设置为从尾部读取的前提是:该文件的最后更新时间小于ignore_inactive设置时间,但是对于秒级甚至ms级持续更新的在线服务日志文件,这个条件判定正常是无法满足的,其并不能完成log类型tail_files对于全部新文件从尾部读取的功能。
总结
最终得出结论:新filestream类型目前并没有提供旧log类型tail_files参数类似的可以控制新加入文件是否从尾部读取的功能,对于持续读写的大log文件首次接入filebeat,可能会由于从头开始同步导致短时QPS飙升进而导致一系列负载飙升,需充分考虑相应的处理方案以防万一。
转载请注明出处,原文地址:https://www.cnblogs.com/AcAc-t/p/filebeat_filestream_read_offset.html
参考
https://www.elastic.co/guide/en/beats/filebeat/7.17/filebeat-input-filestream.html