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

posted @ 2023-09-27 01:27  及时  阅读(474)  评论(0编辑  收藏  举报