资讯动态

基于ES Streams的日志自动解析:用ML替代手写Grok的实战

发布时间:2026/10/19 7:29:32 来源:尧图企业网站定制
最近搭了一套基于 Elasticsearch 的日志处理链路核心思路是让数据流Streams在入索引之前就把日志解析这件事交给机器学习ML去自动完成而不是继续维护一摞手写的 Grok 正则。折腾了几天把踩过的坑、试过行的方案、还有最终落地的配置都整理出来做这个方向的同学可以参考尤其是那些日志格式不固定、字段经常漂移、又不想天天改正则的团队。1. 日志解析这件事为什么要交给机器学习1.1 手写 Grok 的“三天之痒”传统日志解析思路其实很简单日志进来之后通过 Logstash 或者 Ingest Pipeline 里的 Grok 处理器按照预定义的正则模式把一行文本拆成一个个字段。比如把2025-06-01 10:00:00 INFO orderId12345 userId6789拆成timestamp、level、orderId、userId再继续做后续的可观测性或告警分析。这套方案在小规模、格式稳定的场景下没有大问题。但一旦服务多起来痛点就非常明显。我见过一个系统应用日志的格式从 v1 迭代到 v7中间有过字段改名、加前缀、删层级每次迭代总要同步更新解析规则。有时候新版本上线之后日志能正常写进索引但字段解析出来是空的生产环境肉眼看不出来等排查问题时才发现orderId根本没被提取成功。Grok 模式对研发人员来说易写难维护。你把 20 条 Grok 模式放进配置文件三个月后再回来看基本上没有人能一眼说清楚每条模式覆盖了哪些日志格式。更麻烦的是日志格式一旦出现了不可预料的变种正则写不到的情况就只能靠手工补。这种维护成本在日常运维中非常隐形但确实耗人。1.2 哪些日志真正适合 ML 解析并不是所有日志都值得上 ML 解析。我建议你按场景判断符合下面几类特征的日志可以优先考虑来自第三方系统或老系统产出的日志格式杂乱且没有稳定文档可查靠阅读源码去逆向格式成本高。字段名在不同日志行之间不统一但字段值存在明显的语义特征比如交易号、用户编号、IP、URL、错误码等。日志是一个长期的“活”数据后续会不断有新的格式变体或者不同团队的输出风格不同。你希望从日志解析中释放人力而不是每次新增日志格式就安排一次需求排期。反过来如果日志格式高度统一、字段稳定、数量巨大且使用简单 KV 结构手写 Grok 反而是最高效的ML 在这种场景下属于杀鸡用牛刀。1.3 自动化解析的总体架构我最终采用的架构分四层采集层、数据流层、解析层和应用层。采集层就是用 Filebeat、Fluentd 这类组件把日志推送到 Elasticsearch解析层由 Streams 相关的 Ingest Pipeline 完成里面有传统处理器也会调用 ML 能力。应用层再接入异常检测让拆出来的字段参与告警和数据分析。这套架构的好处是日志数据进入 ES 的那一刻已经是结构化的而且解析规则不是硬编码正则而是资产化的模型和处理器配置。后面日志格式变化模型可以在后台重新训练再上线整体上比人肉改配置可持续得多。2. Streams 里的解析链路Data Stream、Ingest 与 ML 怎么配合2.1 Data Stream 给日志带来了什么Data Stream 从设计上就是给这种高频写入、时间敏感、只追加不修改的日志类数据准备的。它把索引细节屏蔽在后面你操作的是一个逻辑数据流。相比直接管理多个分片索引Data Stream 帮你自动处理滚动、生命周期策略和冷热迁移比较适合长期收集日志的场景。在 ML 自动解析这件事上Data Stream 还有一个关键优势数据写入前会强制经过预定的 Ingest Pipeline这个环节正是我们做解析的位置。你不需要再写额外的调度程序去扫描缓冲区的数据每个文档从进来那一刻开始解析流程就自动执行。如果你还没有把日志迁到 Data Stream我建议先把这个做完。它不但简化了索引管理的操作而且把“解析”这件事统一收口到流上所有日志必须过管道。我见过不少团队虽然也上了 ES但日志是直接用 Beats 直连索引解析规则分散在多个采集端配置文件里出了问题很难统一排查。2.2 Ingest Pipeline 是流里最重要的加工车间Ingest Pipeline 是 Streams 数据链路中的核心加工点。每个文档在写入 Data Stream 前都会经过你定义的 Pipeline里面按顺序执行一系列 Processor。Grok、Rename、Set、Script 这些是常客ML 相关的推理处理器也能挂在这里。我的理解是Ingest Pipeline 之所以适合放 ML 解析不是因为它能做复杂计算而是它提供了一种“面向未来的接口”。你可以在管道的早期阶段做格式规整在管道中后期让 ML 模型跑一遍然后在管道的末尾再细化字段。每个步骤职责单一出现问题也比较容易定位。值得注意的是Ingest Pipeline 的中间的处理器数量越多写入延迟就越明显。如果 Pipeline 里既有 Grok 又有多个模型调用建议把高频、低成本的解析步骤放在前面先执行。因为这样可以尽快让文档达到结构形态后面的模型调用即使失败也不至于让整个原始信息被丢弃。2.3 三个 ML 能力我到底该用哪个Elastic Stack 中与日志解析沾边的 ML 能力我实际体验下来主要涉及这三个方向大家容易被概念绕晕做个小对比表ML 能力解决的问题适合场景实时性日志分类把文本聚成相似类别自动泛化变量部分日志格式不稳定、需要先归纳模式准实时推理处理器通过训练模型提取字段内容需要从非结构化文本里抽特征字段准实时异常检测在时间维度上发现规律偏差解析后数据的后续分析不做解析本身实时/准实时在解析阶段它俩的分工也不同。日志分类的强项是把大量文本“聚”出模板来比如它能识别出txId12345和txId67890属于同一类日志只是卡号不同。但它并不会精确地告诉你字段名叫txId它只会把变量位泛化成*。如果要做精确字段提取还是要训练推理模型或者在 Processor 里用正则做兜底。业界也有人直接上生成式模型来做解析但现在在 ES 的生产链路里还不够成熟我不太推荐生产环境冒这个险。通过 ML 做结构化核心价值在于减少人力和规则维护同时保持解析结果可以被追踪、被验证而不是追求百分之百自动化。定位清楚了方案才靠谱。3. 实操把一个真实日志接入 Streams 并让 ML 自动解析3.1 第一步建索引模板与 Data Stream我这里用一个支付类服务的日志来演示虚构的服务名就叫pay-service。日志行的样例是这种风格2025-06-01 10:15:22.134 INFO orderPay txIdTX998132 userIdU7788 amount199.00 statusSUCCESS latency34 2025-06-01 10:16:01.007 WARN retryPay txIdTX998133 userIdU7789 amount99.00 statusRETRY reasontimeout latency1200要建立 Data Stream通常先定义一个索引模板模板里指定data_stream类型、压测用的生命周期策略以及最终要绑定的 Ingest Pipeline。核心模板大致长这样{ index_patterns: [logs-pay-*], data_stream: {}, template: { settings: { default_pipeline: pay-service-pipeline } } }创建完成后把索引命名成logs-pay-defaultES 会自动构成一个名为logs-pay-default的 Data Stream。后续所有往这个流里写的日志都会默认过pay-service-pipeline管道。这里有一个容易踩坑的地方如果你的数据里没有timestamp字段Data Stream 的写入会直接报错。我建议在 Ingest Pipeline 的最前面环节就用date处理器把日志里的时间字符串转成标准时间戳这样能最大程度保障后续解析能用上时间字段。3.2 第二步在 Ingest Pipeline 里做粗解析粗解析的目标不是一步到位而是先把文档“削”成比较规整的结构。我这里用三个 Processor 完成前置工作date解析时间grok提取首层动作和状态convert把已有的数字字符串改成数值类型。下面是一份简化版的 Pipeline 定义{ description: pay-service data pipeline, processors: [ { date: { field: message, target_field: timestamp, formats: [yyyy-MM-dd HH:mm:ss.SSS], timezone: Asia/Shanghai } }, { grok: { field: message, patterns: [ %{TIMESTAMP_ISO8601:log_time} %{LOGLEVEL:level} %{WORD:action} %{GREEDYDATA:raw_detail} ] } }, { script: { lang: painless, source: if (ctx.raw_detail ! null) { ctx.detail ctx.raw_detail; ctx.remove(raw_detail); } } } ] }粗解析完成之后文档里会带出action、level、log_time和detail等字段。接下来再进入真正的 ML 解析环节。需要留意的是Grok 里用了GREEDYDATA这种贪婪模式来承接剩余部分它并不保证细节字段被拆分所以我们还需要后续的模型来补刀。在实际生产里我一般会把grok写尽量简单只提取必需的高置信度字段剩下拿不准器动不了的部分留给模型去处理。不要妄图一个正则吃下所有字段那会把整个 Pipeline 变得特别脆。3.3 第三步创建日志分类任务自动归纳模式粗解析完成后我先启一个日志分类Categorization任务。这个任务通过分析detail字段的文本内容自动把相似的日志行聚合到同一个类目下同时将变量位自动替换为通配符。适用于我们这样字段取值多变但句式固定的场景。创建一个分类任务的 API 是这样的PUT _ml/anomaly_detectors/pay-service-log-cat { analysis_config: { bucket_span: 15m, categorization_field_name: detail, detectors: [ { function: count, by_field_name: mlcategory } ], influencers: [service] }, data_description: { time_field: timestamp } }任务跑起来后Kibana 的 ML 界面会展示自动归纳出来的类目。比如detail里的txIdTX998132和txIdTX998133会被泛化进同一个类目展示为txId*。同时每个类目都有样本日志和分布比率这让后续针对性的字段提取有了依据。如果在界面里发现分类出现了奇怪的碎片比如事务 ID 没有被正确泛化多半是因为日志中存在长度差异过大的数值字段或者存在非常罕见的特殊字符。可以在任务配置里调一下categorization_analyzer的自定义tokenizer把特殊字符也纳入分词范围。我在实践中把默认的standard分词器换成pattern自定义规则后分类准确度有明显提升。有一点要说清楚分类任务的产出本身也可以直接放到告警规则里比如某个新的日志类目在 15 分钟内突然增加那大概率是线上出了问题。这是分类任务带给我们的“附加好处”。3.4 第四步用推理模型自动识别字段分类任务帮忙把日志“归类”了但还没到“字段化”。比如我想拿txId、userId、amount这些字段直接参与聚合查询就得走上模型推理这一步。更常见的做法是在 Ingest Pipeline 里挂一个inference处理器调用已经部署好的命名实体识别模型或自定义文本分类模型。给个示例结构{ inference: { model_id: ner_log_fields_v1, target_field: ml_parsed, field_map: { detail: text_field } } }这个处理器执行后模型在detail里识别出的实体会被放进ml_parsed下的字段里再通过set处理器将结果展开到文档顶层。这里模型的具体类别和标签取决于训练数据我这边用了一个针对交易场景训练过的小模型识别txId、userId、amount这类自定义标签的效果还行。如果你们没有足够的训练数据可以先用 Elastic 自带的一些通用模型分子出场或者退一步用script处理器让正则作为兜底。到这里我特别提一个实操经验模型推理的结果一定要做平滑处理。比如模型识别出的amount字段值可能是带钱的字符串也可能带噪声你必须在后续处理器里做convert转数值同时处理失败兜底为 null。否则后续做数值运算时Kibana 可视化会直接变成空的或者报错排查起来很隐蔽。用 Pipeline 的on_failure分支把解析失败的文档单独送进一个死信索引是排查这类问题的高效做法。3.5 第五步异常检测与效果验证解析链路通了之后不要着急上线先在开发环境做一轮完整的验证。我的验证方式非常直接往 Data Stream 里灌几万条带典型特征的日志然后使用 ES|QL 查询不同类目的分布观察字段是否都被提取成功FROM logs-pay-default | WHERE service pay-service | EVAL tx_id COALESCE(ml_parsed.txId, detail) | STATS count count(*) BY action, status这段查询能快速发现字段缺失或者解析错误的情况比如统计出来的status是不是有太多 null比如amount字段是不是能正常做 AVG 聚合。只要这两个层面过了基本就可以放心交给下游消费。异常检测我放在了解析之后。让 ML 任务持续监控action和status的分布如果某个类目的出现频率突然偏离历史基线就能触发告警。这本质上是把解析能力和诊断能力串联起来第一步把数据整理好第二步从整理好的数据里找偏差。这两步不要混在一起做否则排查问题的时候你会分不清究竟是解析坏了还是数据真的异常了。可以做一下回归确认每个阶段输出的文档样例符合预期。这样上线的时候不仅验证了解析效果也顺带把 Pipeline 性能摸了一遍底。4. 高频问题和性能调优实战4.1 模型加载 OOM解析链路全挂ML 推理是要吃内存的。尤其是 NLP 模型跑在 Node 上堆外内存分配不当容易把节点拖垮。我一开始在三个节点上都部署了模型副本结果高峰期内存告警Pipeline 里的inference处理器大量超时日志写入持续积压。解决思路比较直接控制模型分配节点数不为每个节点都部署副本同时把 Pipeline 里的inference处理器放在后半段让模型处理量尽量低于采集峰值。另外要监控ingest_pipeline_failures和队列积压量这两个指标任何一个出现异常都要及时扩容或者降级处理。更保险的做法是在入口做限流或降级。如果日志量突然暴涨可以先让 Pipeline 使用一个script处理器跳过模型推理先保证日志能写入不丢失然后再异步追补解析。这种牺牲一点实时性换取可用性的策略在生产里比硬扛实际得多。4.2 日志格式一漂移分类就乱日志格式不会永远不变尤其是研发团队在不同版本里改了输出风格之后解析结果就会逐渐失真。日志分类在这时会表现出一种典型症状同一个业务事件被拆分成多个类目或者新格式的日志直接被归类到“其他”里。要根治漂移问题需要树立一个内部约定日志输出格式是接口的一部分变更要走评审。但同时也要让解析机制有一定的容错空间。我的做法是为分类任务建立“已知模板库”当新类目出现时及时与研发确认格式变化再增量训练模型。不要试图一次性训练一个包打天下的模型因为日志类型是线性增长的。另一个细节是分类任务的开启方式。如果分类任务一直开着那么类目列表会随时间自然膨胀。建议将分类任务的summary_count定期清理或者设置周期让模型重新按最新数据聚类这样能避免老旧的、已下线服务的日志模式永久占用标签空间。4.3 字段爆炸与索引开销字段多本身不是问题问题是被模型解析出来的字段如果每个都是独立的动态映射字段索引的 mapping 会迅速膨胀。特别是一些文本字段如果不加节制地开启text子字段存储和查询性能都会受到明显影响。我的经验是把解析出来的字段分成两类一类是高基数且需要聚合的比如txId、userId用keyword类型另一类是自然语言文本用text类型但尽量不开启聚合分析需求这样能显著降低索引开销。Ingest Pipeline 里建动态模板映射的时候可以把这个原则固化下来。另外在 Data Stream 的索引模板中建议给 ML 字段统一加一个前缀比如ml_parsed.*然后用专门的索引模板规则做字段类型统一管理。这样后面清理模型产物字段的时候只需调整模板和索引生命周期即可不用去代码里寻找散落的映射逻辑。4.4 ML 节点与 Ingest 节点的那点事Elasticsearch 中inference处理器在 Ingest Pipeline 里执行但模型的加载位置在 ML 节点上。如果 Ingest 节点和 ML 节点是分开部署的跨节点调用的网络开销与稳定性会直接影响整个解析链路的性能。尤其在高写入量下网络抖动会导致 Pipeline 批量失败进而触发重试风暴。生产环境比较合理的规划是把 Ingest 节点和 ML 节点做一定融合或者保证两者之间有足够的内部带宽。如果你是单集群运维几个服务的小规模团队完全可以允许 Ingest 节点上同时跑轻量 ML 任务但一定要给 JVM 堆外内存留足余量。我给一个粗略的参考值轻量 NER 模型推理并发 4 的时候每个节点的堆外内存至少预留 2GB 以上具体还要以官方性能基准为准。如果模型吞吐真的跟不上日志写入建议在采集端先做预聚合比如把多条同类日志合并为一条摘要日志再进入流。这样虽然损失了部分单条日志的原始细节但保证了整体管道的稳定性是一种典型的流量整形思路。4.5 Pipeline 模型推理失败时的备份方案还有一类问题比较隐蔽模型推理失败了但写入流程并没有中断文档中会保留一个ml_parsed.error标记。很多同学看到索引里数据量还在增长就认为管线是健康的直到后续统计分析时才发现字段大面积缺失。我自己的排查习惯是不只看数据总量而是看管道处理成功率和字段填充率。每次发布新的模型或改 Pipeline都会跑一遍校验查询专门检查各关键字段的 null 占比。如果某字段解析覆盖率低于 95%我会先查训练数据覆盖范围再决定是否补样本、调阈值或降低该字段的提取要求。另外一定要给 Pipeline 配置on_failure。这个分支可以写进死信索引也可以写进一个单独的调试索引反正不要让失败记录悄无声息地混进主数据流里。把失败原因做成可观测数据之后模型迭代才有足够多的依据而不是靠体感调试。我个人在这套实践里最大的体会是把日志解析交给 ML不是一步到位的而是一个持续演进的系统工程。前半程的架构保障了解析能自动跑后半程的数据治理决定了它跑得稳不稳。Streams 只是把数据入口管住了真正让日志产生价值的是精心设计的解析与观测闭环。如果你正被 Grok 模式维护困扰不妨从分类任务和推理模型入手小范围验证再逐步铺开这个过程会比你想象的顺很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑