Java 异常堆栈跨多行,采集时最容易把一条异常拆散。本文给出 Filebeat 的 multiline 配置、输出到炬鲸的两种方式,以及验证排查步骤,10 分钟把多行堆栈日志接进来。
一条 Java 异常通常长这样:第一行是 Exception in thread ...,后面跟着几十行堆栈。Filebeat 默认按行读取,一行就是一条事件。如果不做多行合并,堆栈的每一行都会被当成独立日志发出去,告警只会命中第一行,排查时还得靠 grep 把上下文拼回来——这在生产环境基本不可用。
核心是用 multiline 让"不以日期开头的行"并入上一条。以 filestream 输入为例:
filebeat.inputs:
- type: filestream
paths:
- /var/log/app/*.log
multiline.type: pattern
multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
multiline.negate: true
multiline.match: after
含义:匹配不到 2026-10-13 这种日期开头时(negate: true),把当前行接到上一条事件之后(match: after)。这样一条完整异常会被合并成一个事件,直到下一行再次以日期开头才截断。
老版本 log 输入写法一致,只是 multiline 与 paths 同层。如果堆栈里也有以日期开头的行,建议把 pattern 收窄到日志框架的实际前缀,例如 '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'。pattern 太宽会把两条异常合并成一条,太窄又会把堆栈拆散,拿真实样本文件试一遍再上线。
方式一,Filebeat 直接发 HTTP。炬鲸提供采集端点,配置 output.http:
output.http:
hosts: ["https://ob.jjhub.cn/api/ingest/logs"]
headers:
Authorization: "Bearer <token>"
compression_level: 6
方式二,先入 Kafka 再走采集器。量大或需要削峰时更稳,下游消费端抖动时还能重放 topic:
output.kafka:
hosts: ["kafka1:9092", "kafka2:9092"]
topic: app-logs
partition.round_robin:
reachable_only: true
炬鲸侧配好对应 topic 的消费者即可。两种方式都能保留原始堆栈的多行完整性,选哪种只取决于吞吐和投递保障要求,不影响数据本身。
接完后别急着走,做三件事:
multiline.timeout(默认 5s)。如果一条异常写到一半停了,超过超时会强制截断,长时间运行的线程 dump 建议调大。filebeat.harvester 指标能看出是否频繁重读文件,避免重复采集。再补一条时区提醒:日志时间戳不带时区时,采集端和炬鲸可能差 8 小时,统一在采集端用 processors 补时区。排查口诀:先看事件数量对不对,再看字段拆没拆,最后看堆栈全不全。三步过了,多行日志就接稳了。