03-Logstash 数据管道配置
前一篇把 Elasticsearch 装起来,并明确了数据流、模板、权限和生命周期。本文接着处理中间层:Logstash。它不是所有日志链路都必须出现的组件,但当日志来源复杂、字段需要治理、解析失败需要复盘、写入前需要脱敏或路由时,Logstash 仍然是 ELK Stack 里最适合承。

前一篇把 Elasticsearch 装起来,并明确了数据流、模板、权限和生命周期。本文接着处理中间层:Logstash。它不是所有日志链路都必须出现的组件,但当日志来源复杂、字段需要治理、解析失败需要复盘、写入前需要脱敏或路由时,Logstash 仍然是 ELK Stack 里最适合承担“可审计数据加工”的位置。
这篇不把 Logstash 写成插件百科。我们用一条典型链路贯穿全文:Filebeat 把应用日志送到 Logstash,Logstash 解析、规范化、脱敏、打标签,再写入 Elasticsearch 的 data stream。中间会覆盖安装、管道语法、grok/date/mutate/json、持久化队列、死信队列、配置验证、监控 API 和交付清单。代码只保留能直接支撑这条链路的部分。
本文参考当前 Elastic Logstash 文档。官方 9.x 包仓库已经按大版本拆分,APT 源使用 packages/9.x/apt;Logstash 管道配置由 input、filter、output 三类插件段组成;持久化队列默认关闭,开启后会把在途事件放到本地磁盘;死信队列默认关闭,用来暂存无法正常处理的事件。版本细节会变,生产落地前仍要按目标版本文档复核。
先判断是否需要 Logstash
图:Logstash 的价值在输入、过滤、输出之间形成可验证的数据加工边界,而不是替代所有采集组件。
现代 Elastic 体系里,Filebeat、Elastic Agent 和 Elasticsearch ingest pipeline 已经能覆盖很多轻量场景。如果只是把标准 Nginx 日志、系统日志或容器日志送进 Elastic,优先考虑 Agent/Beats 加集成模板;如果只是简单字段重命名,也可以放在 ingest pipeline。Logstash 适合更重的中心化处理。
可以用这张表决定:
| 场景 | 是否适合 Logstash | 原因 |
|---|---|---|
| 多来源日志格式差异大 | 适合 | 集中解析和标准化,便于统一模板 |
| 需要复杂条件路由 | 适合 | 可以按字段、标签、环境、错误类型分流 |
| 写入前要脱敏 | 适合 | 在入库前处理敏感字段,减少泄露面 |
| 需要缓冲和死信处理 | 适合 | PQ/DLQ 能提供排障证据 |
| 单一标准日志采集 | 不一定 | Filebeat 或 Agent 更轻 |
| 只做展示查询 | 不适合 | Kibana 和 Elasticsearch 承担 |
Logstash 的成本也要明确。它运行在 JVM 上,资源占用高于 Beats;配置语言灵活,但错误配置也更容易造成字段冲突、重复写入或吞吐瓶颈。把 Logstash 放进链路,意味着团队要维护 pipeline、插件版本、队列磁盘、证书、账号和监控指标。
所以本文的目标不是“所有日志都经过 Logstash”,而是建立一条可接管的中心处理管道:输入可信,解析有证据,失败可隔离,输出有权限,变更可回滚。
管道的基本合同
Logstash 配置文件的核心结构很简单:
input {
# 从哪里来
}
filter {
# 如何解析、转换、脱敏、路由
}
output {
# 写到哪里去
}
input 和 output 是必须的,filter 是可选的。但在真实日志治理里,filter 往往是最关键的部分。它决定一条原始日志最终变成怎样的结构化事件,字段类型是否稳定,时间戳是否正确,敏感内容是否被清理,失败能否被识别。
管道配置不是脚本文件。它更像一份数据合同:输入端承诺传入什么格式,过滤端承诺输出哪些字段,输出端承诺写到哪个数据流。合同不清楚,后面的 Kibana 看板、告警和自动化分析都会反复出问题。
一个生产管道至少要定义这些边界:
| 边界 | 要回答的问题 |
|---|---|
| 输入 | 数据从 Beats、TCP、HTTP、Kafka 还是文件进入 |
| 格式 | 原始日志是 JSON、Nginx 文本、syslog 还是自定义格式 |
| 时间 | @timestamp 使用事件时间还是采集时间 |
| 字段 | 哪些字段进入 ECS,哪些字段保留为业务字段 |
| 脱敏 | token、密码、手机号、身份证号如何处理 |
| 失败 | 解析失败、mapping 冲突、写入失败如何留下证据 |
| 输出 | 写入哪个 data stream,用什么账号和 CA |
本文示例用 Beats 输入。原因很简单:在实际部署中,不建议让 Logstash 直接去每台机器 tail 文件。Filebeat 更适合贴近主机采集,Logstash 更适合集中处理。这样主机侧轻量,中心侧可治理。
安装与目录
Debian/Ubuntu 环境可以使用官方 APT 仓库。当前 9.x 仓库示例:
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch \
| sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/9.x/apt stable main" \
| sudo tee /etc/apt/sources.list.d/elastic-9.x.list
sudo apt-get update
sudo apt-get install logstash
安装后先认识目录:
| 路径 | 用途 |
|---|---|
/etc/logstash/logstash.yml |
Logstash 全局设置 |
/etc/logstash/pipelines.yml |
多管道入口 |
/etc/logstash/conf.d/ |
默认管道配置目录 |
/usr/share/logstash/bin/logstash |
命令行入口 |
/var/lib/logstash/ |
数据、队列、死信队列默认位置 |
/var/log/logstash/ |
运行日志 |
不要把管道配置散落在个人目录。生产环境建议把 /etc/logstash 纳入配置管理,把 pipeline 文件放入 Git,变更通过评审、测试、发布和回滚。Logstash 的一次配置变更可能影响所有日志写入,不能靠 SSH 上去手改。
安装完成后验证版本和服务:
/usr/share/logstash/bin/logstash --version
sudo systemctl enable logstash.service
sudo systemctl status logstash.service
服务不一定要马上启动。更好的顺序是先写好最小 pipeline,执行语法检查,再启动服务。否则 Logstash 会加载默认或空配置,问题不明显。
从最小管道开始
最小管道用于确认 Logstash 能解析配置、接收输入并输出事件:
input {
stdin {}
}
output {
stdout {
codec => rubydebug
}
}
命令行验证:
/usr/share/logstash/bin/logstash \
-e 'input { stdin {} } output { stdout { codec => rubydebug } }'
输入一行文本后,Logstash 会输出包含 message、@timestamp、@version 等字段的事件。这个测试不代表业务管道成功,只证明运行环境、JVM 和基本配置语言没有问题。
接下来进入真实链路:Beats 输入、过滤处理、Elasticsearch 输出。为了让示例可读,先定义目标事件:
{
"@timestamp": "2026-06-11T10:15:30.000Z",
"service.name": "payment-api",
"log.level": "ERROR",
"event.dataset": "app.log",
"host.name": "app-01",
"message": "payment failed",
"trace.id": "8f3a1c9d",
"labels.env": "prod"
}
这不是所有字段的最终形态,但它表达了核心要求:时间正确、服务可识别、级别可聚合、数据集可区分、主机可定位、原始消息仍可追溯。
Beats 输入
中心化 Logstash 常见输入是 Beats:
input {
beats {
port => 5044
ssl_enabled => true
ssl_certificate => "/etc/logstash/certs/logstash.crt"
ssl_key => "/etc/logstash/certs/logstash.key"
}
}
如果学习环境还没有证书,可以先在内网临时关闭 TLS,但生产不应这样做。Filebeat 到 Logstash 的链路可能包含业务日志、异常堆栈、请求参数和内部地址,裸传输会扩大风险。上一篇 Elasticsearch 已经强调证书和账号,Logstash 输入端也要保持同一标准。
Beats 输入适合做中心接入,因为它能和 Filebeat 形成背压关系。Logstash 处理变慢时,Beats 输入可以阻止无限制接入,避免中心节点内存被瞬间打爆。这一点和 UDP/TCP 裸输入不同。UDP 没有确认机制,丢了就丢了;TCP 虽然可靠一些,但应用侧协议和重放语义仍要自己处理。
输入端不要做太多业务判断。它只负责接入和基础标识,例如端口、TLS、来源标签。复杂解析应放在 filter,这样后续从 Beats 改成 Kafka 或 HTTP 时,解析逻辑不需要完全重写。
解析 JSON 日志
如果应用能输出 JSON 日志,优先使用 JSON。它比 grok 文本解析稳定得多,也更容易和 ECS 对齐。示例原始日志:
{"time":"2026-06-11T10:15:30+08:00","level":"ERROR","service":"payment-api","trace_id":"8f3a1c9d","message":"payment failed","password":"secret"}
对应 filter:
filter {
json {
source => "message"
target => "app"
tag_on_failure => ["_json_parse_failure"]
}
if "_json_parse_failure" not in [tags] {
mutate {
rename => {
"[app][service]" => "[service][name]"
"[app][level]" => "[log][level]"
"[app][trace_id]" => "[trace][id]"
}
add_field => {
"[event][dataset]" => "app.log"
"[labels][env]" => "prod"
}
remove_field => ["[app][password]"]
}
date {
match => ["[app][time]", "ISO8601"]
target => "@timestamp"
}
}
}
这里有几个关键点。第一,JSON 解析结果放到 app 下,避免直接污染顶层字段。第二,字段重命名时尽量向 ECS 靠拢,比如 service.name、log.level、trace.id。第三,敏感字段在写入前移除。第四,解析失败要打标签,而不是悄悄吞掉。
不要为了字段少就删除原始 message。排障时,原始日志经常是最后证据。更稳妥的做法是保留 message,同时把结构化字段展开。只有在明确存储成本和合规要求后,才考虑裁剪原文。
解析文本日志
不是所有系统都能输出 JSON。Nginx、老 Java 应用、设备日志和第三方系统经常还是文本。文本解析可以用 grok,但要控制复杂度。Grok 表达式越复杂,越容易慢、错、难维护。
典型 Nginx access log:
10.0.0.1 - - [11/Jun/2026:10:15:30 +0800] "GET /api/orders HTTP/1.1" 200 532 "-" "curl/8.0"
简化 filter:
filter {
grok {
match => {
"message" => '%{IPORHOST:[client][ip]} %{USER:ident} %{USER:auth} \[%{HTTPDATE:nginx_time}\] "%{WORD:[http][request][method]} %{URIPATHPARAM:[url][path]} HTTP/%{NUMBER:[http][version]}" %{NUMBER:[http][response][status_code]:int} (?:%{NUMBER:[http][response][body][bytes]:int}|-) "%{DATA:[http][request][referrer]}" "%{DATA:[user_agent][original]}"'
}
tag_on_failure => ["_grok_nginx_failure"]
}
date {
match => ["nginx_time", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
mutate {
add_field => {
"[event][dataset]" => "nginx.access"
}
remove_field => ["ident", "auth", "nginx_time"]
}
}
这段配置只解析常用字段,不追求一次覆盖所有边界。生产管道应该先用样本集验证:正常请求、404、500、body bytes 为 -、URL 带 query、User-Agent 为空、字段里有引号。每类样本都过了,再发布。
如果 grok 规则持续膨胀,要考虑两个替代方案:推动应用输出 JSON,或者把格式固定的日志交给 Elastic 集成。Logstash 能解析复杂文本,但不应该成为所有日志格式债务的垃圾桶。
文本日志还有一个经常被低估的问题:多行事件。Java 堆栈、Python traceback、数据库错误和容器启动日志都可能跨多行。如果采集端逐行发送,Logstash 看到的就不是一条完整异常,而是一堆互不关联的碎片。后续 Kibana 搜索、告警和自动化分析都会失去上下文。
多行合并通常更适合放在 Filebeat 侧完成,因为 Filebeat 离文件最近,能在发送前把同一个异常合并成一个事件。Logstash 也能处理多行,但中心化处理时更容易受不同来源格式影响。无论放在哪里,都要先定义清楚边界:哪些日志以时间戳开头,哪些行属于上一条事件,最大合并行数是多少,超过限制时是否截断。
例如 Java 异常可以按“以日期开头的新行代表新事件”来合并。进入 Logstash 后,filter 只负责解析合并后的完整 message,并提取 log.level、service.name、error.type、error.message 等字段。不要让 grok 同时承担“合并多行”和“解析业务字段”两件事。责任拆开后,问题更容易定位:合并错了看采集端,字段错了看 Logstash。
还要保留解析失败样本。每次遇到 _grok_nginx_failure 或 _json_parse_failure,都应该抽取一小批真实样本进入测试集。这样 pipeline 会随着真实日志演进,而不是靠想象维护。日志格式不是静态的,业务服务升级、框架升级、网关配置变化都会改变输出形态。测试样本就是管道配置的回归资产。
条件分支和路由
Logstash 支持条件判断。条件的用途不是把管道写成大程序,而是把不同日志类型分开处理。
filter {
if [event][dataset] == "nginx.access" {
mutate {
add_tag => ["web"]
}
} else if [service][name] == "payment-api" {
mutate {
add_tag => ["business-critical"]
}
} else {
mutate {
add_tag => ["unknown-dataset"]
}
}
}
条件分支最怕类型不一致。例如一个字段有时是字符串,有时是整数,比较时可能失败。死信队列文档也明确提到,条件判断处理事件时出错,事件可能进入 DLQ。为了避免这种问题,先做类型转换,再做比较;不确定存在的字段,先判断字段是否存在。
更复杂的场景建议拆成多管道,而不是在一个文件里堆几百行 if/else。多管道可以隔离资源和故障,也方便按数据集独立发布。
- pipeline.id: app_logs
path.config: "/etc/logstash/pipelines/app/*.conf"
pipeline.workers: 2
queue.type: persisted
- pipeline.id: nginx_logs
path.config: "/etc/logstash/pipelines/nginx/*.conf"
pipeline.workers: 2
queue.type: persisted
拆管道不是为了显得复杂,而是为了让失败边界清晰。应用日志解析坏了,不应影响 Nginx access log;审计日志需要更高可靠性,也不应和 debug 日志共用同一套队列策略。
进一步拆分时,可以使用 pipeline-to-pipeline 模式。常见做法是入口管道只负责接收和粗分类,把事件按数据集发送给内部管道;后续管道分别处理应用、Nginx、审计或安全日志。这样可以把 TLS 接入、认证、背压和业务解析分开。
概念示例:
input {
beats {
port => 5044
}
}
output {
if [event][dataset] == "nginx.access" {
pipeline {
send_to => "nginx_pipeline"
}
} else {
pipeline {
send_to => "app_pipeline"
}
}
}
下游管道再用 pipeline input 接收。这个模式适合大型中心 Logstash,但小团队不必一开始使用。它的好处是隔离配置和资源,代价是排障链路变长。上线前要能回答一个事件从入口到下游经历了哪些 pipeline,否则出现丢失时很难追踪。
多管道还有一个治理价值:发布节奏可以按数据集拆开。Nginx pipeline 的变更不需要和应用 pipeline 一起发布;审计 pipeline 可以要求更严格评审;临时验证 pipeline 可以设置过期时间。随着日志平台扩大,这种边界比单个大配置文件更可维护。
输出到 Elasticsearch
输出端要和上一篇的 Elasticsearch 安装配置衔接。至少要配置 hosts、CA、认证方式、目标 data stream 或索引、失败处理。
output {
if "_json_parse_failure" in [tags] or "_grok_nginx_failure" in [tags] {
elasticsearch {
hosts => ["https://es-prod-01:9200"]
cacert => "/etc/logstash/certs/http_ca.crt"
api_key => "${LOGSTASH_DLQ_API_KEY}"
index => "logs-parse-failed-%{+YYYY.MM.dd}"
}
} else {
elasticsearch {
hosts => ["https://es-prod-01:9200"]
cacert => "/etc/logstash/certs/http_ca.crt"
api_key => "${LOGSTASH_WRITE_API_KEY}"
data_stream => true
data_stream_type => "logs"
data_stream_dataset => "%{[event][dataset]}"
data_stream_namespace => "%{[labels][env]}"
}
}
}
这里用环境变量传入 API Key,避免把凭证写进配置文件。生产中还可以使用 Logstash keystore 管理敏感值。重点是最小权限:正常写入账号只能写目标日志数据流,解析失败账号只能写失败索引或失败数据流,不要使用 elastic 超级用户。
解析失败是否写入 Elasticsearch,要看团队策略。完全丢弃最省成本,但失去证据;全部写入失败索引最便于复盘,但会占用存储并可能包含敏感信息。较稳妥的做法是写入受限失败数据流,设置短生命周期,并对敏感字段先做基础脱敏。
输出端还要理解 Elasticsearch output 的失败语义。整个 HTTP 请求失败时,插件可能持续重试;bulk 请求中单条文档失败时,如果配置了 DLQ,失败事件可以进入死信队列。不能把“开启 DLQ”理解为所有失败都自动解决。DLQ 只提供暂存和调查入口,仍需要人工或自动化流程处理。
输出配置还必须和 Elasticsearch 模板协同。Logstash 可以把字段写出去,但不能替 Elasticsearch 自动决定所有字段语义。上一篇已经建立了 data stream、索引模板和生命周期策略,本篇的 output 必须落到那些约定上。比如 data_stream_dataset 应该是可控集合,而不是任意从日志里透传;data_stream_namespace 应该来自环境或组织边界,而不是用户输入。
如果允许业务日志直接决定目标索引名,会带来两个风险。第一,恶意或异常字段可能创建大量索引,造成集群状态膨胀。第二,不同服务会绕过平台模板,导致 mapping 不一致。更稳妥的方式是 Logstash 根据受控字段路由,未知数据集写到隔离区,等确认模板后再纳入正式数据流。
对于关键业务,可以考虑确定性文档 ID,减少重复写入。比如 Filebeat 重试、Logstash 重启、PQ 重放都可能造成至少一次投递。日志类数据通常接受重复少量事件,但审计或计费相关数据不一定能接受。此时可以用 fingerprint filter 基于来源文件、offset、时间和消息摘要生成 ID,再在 output 里作为 document_id。这会降低写入吞吐,是否使用要按场景判断。
filter {
fingerprint {
source => ["[log][file][path]", "[log][offset]", "message"]
target => "[@metadata][document_id]"
method => "SHA256"
}
}
不是所有日志都需要这一步。对高吞吐 access log,重复少量事件通常比生成 ID 的成本更容易接受;对审计日志、自动化处理结果和告警事件,确定性 ID 更有价值。Logstash 配置要体现数据价值,而不是一套规则套所有数据。
持久化队列
图:配置发布前要先通过语法、样本、队列和输出验证,再让生产采集端切流。
默认情况下,Logstash 使用内存队列。吞吐高,但进程异常退出时在途事件可能丢失。持久化队列会把在途事件写到本地磁盘,Logstash 重启后尝试继续投递。官方文档也提醒,PQ 可以吸收突发流量,但默认关闭,而且不能解决磁盘损坏、机器丢失、未 checkpoint 数据等所有问题。
启用示例:
queue.type: persisted
path.queue: /var/lib/logstash/queue
queue.max_bytes: 8gb
多管道时要注意,队列容量按管道计算,不是全局共享。如果三个 pipeline 都设置 queue.max_bytes: 8gb,磁盘至少要按 24GB 加上余量规划。队列目录必须使用本地文件系统,不要放到 NFS。日志平台遇到 Elasticsearch 短暂不可用时,PQ 可以争取恢复时间;但如果 Elasticsearch 长时间不可用,队列写满后仍会对输入端形成背压。
PQ 的设计要结合业务价值。审计日志、支付错误、核心应用日志可以启用更大的队列;低价值 debug 日志可以短队列甚至直接限流。可靠性不是无限堆磁盘,而是把不同数据价值分级。
队列容量要按“下游最长可接受不可用时间”估算,而不是拍脑袋。假设某条 pipeline 平均每小时接收 20GB 原始事件,团队希望 Elasticsearch 短暂维护 2 小时内不丢核心日志,那么队列容量至少要覆盖 40GB,再加上序列化开销和安全余量。如果磁盘只有 50GB,配置 queue.max_bytes: 64gb 没有意义,最终只会把节点拖进磁盘告警。
PQ 也会影响事件重复。Logstash 异常退出后,未确认的批次可能被重新处理。大多数日志分析场景能接受少量重复,但如果输出端触发工单、邮件或 Webhook,就必须谨慎。Logstash 更适合把事件写入存储系统,不适合直接把每条日志变成不可幂等的外部动作。需要触发动作时,最好在 Elasticsearch 或告警系统里基于聚合结果做。
维护 PQ 时要监控三个信号:队列大小、队列增长速度、最老事件滞留时间。队列短时间增长可能只是下游重启;持续增长说明系统吞吐已经小于输入速率。只看 Logstash 进程 alive 没意义,队列堆积才是中心处理层的真实压力。
死信队列
死信队列用于处理“事件已经进入 Logstash,但无法正常处理或写入”的情况。典型原因是 Elasticsearch mapping 冲突、单条 bulk item 失败、条件判断出错。DLQ 默认关闭,需要显式启用:
dead_letter_queue.enable: true
path.dead_letter_queue: /var/lib/logstash/dead_letter_queue
dead_letter_queue.max_bytes: 1gb
dead_letter_queue.retain.age: 2d
DLQ 不是垃圾桶。它是待处理清单。进入 DLQ 的事件包含原事件、失败原因、插件信息和进入时间。处理方式通常是另起一个 pipeline 读取 DLQ,输出到 stdout 调查,或修正字段后重新写入目标索引。
示例调查管道:
input {
dead_letter_queue {
path => "/var/lib/logstash/dead_letter_queue"
pipeline_id => "app_logs"
commit_offsets => true
}
}
output {
stdout {
codec => rubydebug {
metadata => true
}
}
}
如果确认是某个字段 mapping 错误,可以在重放管道里移除或重命名字段,再写入修复后的数据流。但要谨慎:DLQ 事件再次处理失败时,不一定会重新进入同一个 DLQ。重放前最好先在测试索引验证。
DLQ 还要配合告警。死信队列持续增长,说明解析、模板或写入权限存在问题;只开启不监控,会把线上故障延迟成存储问题。
DLQ 处理流程要像小型事故流程。第一步确认增长来源,是某个 pipeline、某个数据集还是某个字段;第二步抽样查看元数据,判断是 mapping、权限、条件判断还是字段格式;第三步修复 pipeline 或 Elasticsearch 模板;第四步决定是否重放;第五步清理已消费段并记录样本。没有这个流程,DLQ 会变成“没人看的失败仓库”。
重放时不要直接写回原目标。建议先写到临时数据流或测试索引,验证字段结构和文档数量,再决定是否 reindex 或重新投递正式数据流。如果失败原因是敏感字段或错误脱敏,重放前还要重新评估数据是否允许入库。DLQ 里的数据往往正是异常数据,不能默认安全。
DLQ 的保留时间也要和响应能力匹配。设置 2 天保留,但团队只有每周检查一次,等于大部分失败都来不及处理;设置 30 天保留但没有容量告警,又会把磁盘写满。合理做法是短保留加高优先级告警:失败出现就处理,而不是长期囤积。
文件采集验证
图:文件采集验证要覆盖文件变化、采集端发送、Logstash 解析、Elasticsearch 写入和 Kibana 查询。
虽然生产更推荐 Filebeat 采集文件,但学习 Logstash 时可以用 file input 理解事件流。这个方式适合本机验证,不适合大规模主机采集。
input {
file {
path => "/tmp/demo-app.log"
start_position => "beginning"
sincedb_path => "/tmp/logstash-demo.sincedb"
codec => json
}
}
filter {
mutate {
rename => {
"service" => "[service][name]"
"level" => "[log][level]"
}
add_field => {
"[event][dataset]" => "app.log"
"[labels][env]" => "lab"
}
}
date {
match => ["time", "ISO8601"]
target => "@timestamp"
}
}
output {
stdout {
codec => rubydebug
}
}
写入测试日志:
echo '{"time":"2026-06-11T10:15:30+08:00","service":"demo-api","level":"INFO","message":"hello logstash"}' >> /tmp/demo-app.log
这里故意使用独立 sincedb_path,方便反复测试。生产 file input 的 sincedb 不要乱删,否则可能重复采集。Filebeat 也有自己的 registry 机制,本质上都是为了记录采集进度。采集状态文件属于运行状态,不应该放在临时目录里随系统清理。
配置验证和发布
每次变更 pipeline,先做语法检查:
/usr/share/logstash/bin/logstash \
-f /etc/logstash/conf.d \
--config.test_and_exit
如果使用多管道,也要检查 pipelines.yml 是否指向正确目录。语法通过只说明能加载,不说明解析正确。因此还要准备样本集。样本集至少包含:
| 样本 | 目的 |
|---|---|
| 正常 JSON 日志 | 验证主路径 |
| 缺字段日志 | 验证默认值和标签 |
| 非法 JSON | 验证解析失败标签 |
| 异常堆栈 | 验证多行或原文保留 |
| 敏感字段 | 验证脱敏 |
| 字段类型变化 | 验证 mapping 风险 |
样本验证可以用 stdin/stdout,不必每次都写入 Elasticsearch。先让 Logstash 输出 rubydebug,确认字段结构,再接 Elasticsearch 输出。这样能把“解析问题”和“写入问题”分开。
发布时建议按三步走:先在测试 Logstash 运行新配置,再让少量 Filebeat 指向新 pipeline,最后扩大到全量。不要在生产主 Logstash 上一次替换所有 pipeline。配置看起来只是文本,但它处理的是持续流量,错误会迅速放大。
如果启用了 config.reload.automatic,也要谨慎。自动 reload 能减少重启,但不能替代测试。错误配置可能导致 pipeline reload 失败或旧配置继续运行,必须通过日志和监控 API 确认当前加载的是哪份配置。
配置发布还要处理顺序问题。很多 pipeline 变更需要和 Elasticsearch 模板变更配套:先创建或更新模板,再发布 Logstash;如果先发布 Logstash,新的字段可能写进旧 mapping,引发冲突。反过来,如果先删除旧字段模板,但 Logstash 还在输出旧字段,也会造成失败。发布单要写清楚依赖顺序。
一次标准变更可以拆成:
- 准备样本和预期输出;
- 在测试环境更新模板;
- 用 stdout 验证 Logstash 字段;
- 写入测试数据流验证 mapping;
- 灰度一小部分采集端;
- 观察 events、queue、DLQ 和 Elasticsearch 写入错误;
- 全量切换并保留回滚配置。
回滚不一定只是恢复旧文件。如果新配置已经写入了错误字段,回滚 Logstash 只能阻止继续扩大,不能修复已写入数据。必要时还要删除测试数据流、修正模板、reindex 或标记异常时间窗口。文章前面强调样本验证,就是为了降低这种代价。
监控与容量
Logstash 默认提供 HTTP API,常用于查看节点和 pipeline 状态:
curl http://localhost:9600/
curl http://localhost:9600/_node/pipelines
curl http://localhost:9600/_node/stats/pipelines
关注指标:
| 指标 | 含义 | 判断 |
|---|---|---|
events.in |
输入事件数 | 应持续增长 |
events.out |
输出事件数 | 长期应接近输入 |
events.filtered |
过滤后事件数 | 异常下降要查 drop/失败 |
duration_in_millis |
处理耗时 | 突增可能是 filter 或 output 慢 |
| queue events | 队列堆积 | 持续增长说明下游或处理瓶颈 |
| DLQ 大小 | 死信事件量 | 增长说明失败未处理 |
容量调优先找瓶颈,不要上来就加 worker。Grok 复杂、DNS/GeoIP 查询慢、Elasticsearch 写入慢、磁盘队列满、JVM GC 都可能造成吞吐下降。盲目增加 pipeline.workers 可能让 Elasticsearch 承压更大,也可能让事件顺序变化。
一条可用的调优路径是:
- 先确认 Elasticsearch 写入健康,避免把下游问题当成 Logstash 问题;
- 再看 Logstash pipeline stats,定位是 input、filter 还是 output 慢;
- 对 filter 做样本压测,尤其是 grok、ruby、geoip;
- 调整 batch、workers 和 PQ 前先记录基线;
- 每次只改一个参数,并保留回滚值。
Logstash 是中心节点时,还要监控 CPU、heap、磁盘、队列目录、DLQ 目录和 5044/9600 端口。队列目录磁盘满会直接影响管道,DLQ 目录满会掩盖真实失败。
性能基线要在上线前建立。至少记录三组数据:空 filter 的输入输出吞吐、真实 filter 的输入输出吞吐、Elasticsearch 短暂不可用时 PQ 增长速度。没有基线,后续“Logstash 变慢”只能靠感觉判断。基线不需要复杂,固定样本、固定时间窗口、固定节点规格即可。
Grok 是常见性能热点。复杂正则、回溯严重的模式、多个候选 match 都会拉高 CPU。优化时优先减少正则复杂度,能用 dissect 处理固定分隔格式时就不要用 grok;能让应用输出 JSON 时就不要继续解析文本;能在 Filebeat 或 Agent 集成里解决的标准日志,不要强行回到 Logstash 中心解析。
Output 慢时,先看 Elasticsearch。Bulk 写入失败、磁盘水位、线程池拒绝、mapping 冲突都会让 Logstash 看起来像瓶颈。中心链路要同时监控两端:Logstash events.out 下降时,去 Elasticsearch 看 indexing pressure、thread pool、磁盘和集群健康。单看 Logstash 无法解释全链路。
资源隔离也很关键。不要把 Logstash 和 Elasticsearch 数据节点放在同一台低规格机器上跑生产流量。两者都会吃 CPU、内存和磁盘 IO,一起部署时故障会互相放大。学习环境可以同机,生产至少要评估中心处理节点和存储节点的资源边界。
安全与脱敏
Logstash 处在数据进入 Elasticsearch 之前,是脱敏的关键位置。不要把脱敏放到 Kibana 展示层,因为数据已经入库;也不要完全依赖应用开发自觉,因为不同团队标准不一。
常见脱敏可以使用 mutate/gsub:
filter {
mutate {
gsub => [
"message", "password=[^&\\s]+", "password=***",
"message", "token=[^&\\s]+", "token=***"
]
}
}
这只是基础示例。真实脱敏要结合日志格式,避免误伤正常字段,也要覆盖结构化字段而不只是 message。对特别敏感的数据,最好在采集前或应用侧就不要输出。Logstash 可以补救,但不应该成为唯一防线。
凭证管理也属于安全。Elasticsearch API Key、证书私钥、Kafka 密码、HTTP 输出 token 都不能写进普通配置文件。可以使用环境变量、Logstash keystore 或平台密钥管理。配置仓库只保存变量名和引用方式。
网络上,Beats 输入端口只对采集端开放,监控 API 9600 只对运维网络开放。不要把 9600 暴露给普通业务网络,它可能泄露 pipeline、插件和运行信息。
还要明确哪些字段不允许进入日志平台。比如完整 Authorization header、Cookie、数据库连接串、云厂商 AK/SK、用户身份证号、银行卡号、未脱敏手机号,都应在应用侧或采集侧尽早处理。Logstash 可以做二次兜底,但如果所有敏感信息都先进入中心节点,再靠 filter 删除,中心节点本身已经承担了更高合规风险。
脱敏规则也要测试。正则过宽会把正常业务字段替换掉,正则过窄会漏掉真实凭证。可以准备一组“应脱敏”和“不应脱敏”的样本,用 stdout 输出验证。安全规则和解析规则一样,也需要回归测试。
常见故障
Logstash 故障要按阶段定位:
| 表现 | 可能阶段 | 排查方向 |
|---|---|---|
| Beats 连接失败 | input | 端口、TLS、证书、防火墙 |
| 日志进入但字段不对 | filter | json/grok/date/mutate 顺序 |
@timestamp 不准 |
filter | date match、时区、字段来源 |
| 写入 401/403 | output | API Key、索引权限、CA |
| 写入 400 | output/filter | mapping 冲突、字段类型 |
| 队列持续增长 | output 或 filter | ES 慢、filter 慢、下游不可用 |
| DLQ 增长 | output/filter | 单条失败、条件错误、mapping |
最常见的错误是把所有问题都看成 grok 失败。实际上写入 400 往往是 Elasticsearch mapping 已经固定,Logstash 只是暴露了字段类型冲突。此时改 grok 不一定能解决,可能要修模板、修历史索引、写新字段或重建数据流。
第二个常见错误是丢弃失败事件。drop {} 适合明确不要的数据,比如健康检查日志、调试噪声;不适合处理解析失败。解析失败应该打标签、写失败数据流或 DLQ,至少留一段时间给团队分析。
第三个错误是把一个 pipeline 承载所有日志类型。最初方便,后面会变成变更风险。某个业务的字段调整,可能影响所有日志;某个 grok 慢,拖累整条管道。按数据集拆分 pipeline,是从试点走向生产时必须做的事。
第四个错误是只在 Logstash 上修数据质量。字段命名混乱、日志级别不规范、时间戳缺失、异常堆栈不完整,本质上很多是应用日志规范问题。Logstash 可以标准化和兜底,但不能无限替应用补债。平台团队应该把反复出现的解析问题反馈给业务团队,推动应用输出结构化日志。
第五个错误是忽略时区。应用日志可能是本地时间,容器日志可能是 UTC,Nginx 可以带时区,数据库日志可能不带时区。date filter 配错后,数据会写入错误时间窗口,Kibana 看起来像“没有数据”。处理方式是让源日志尽量输出 ISO8601 带时区时间;做不到时,在 Logstash 中为特定数据集显式声明 timezone,并把这个假设写进文档。
第六个错误是把 stdout 调试配置留在生产。stdout { codec => rubydebug } 很适合本地测试,但生产高流量下会产生大量日志,影响性能并泄露数据。上线前要检查 output,只保留必要目标和受控失败输出。
交付清单
Logstash 管道上线前,至少留下这些证据:
| 项目 | 证据 |
|---|---|
| 安装 | 版本、安装源、服务状态 |
| 输入 | Beats/TCP/HTTP/Kafka 来源、端口、TLS |
| 解析 | 样本集、字段映射、失败标签 |
| 输出 | 目标 data stream、账号权限、CA |
| 队列 | PQ 是否开启、容量、磁盘告警 |
| 死信 | DLQ 是否开启、处理流程、保留时间 |
| 监控 | 9600 API、事件速率、队列、DLQ |
| 发布 | 语法检查、灰度、回滚路径 |
| 安全 | 脱敏规则、凭证管理、网络范围 |
这份清单的目的不是让文章更完整,而是让后续 Kibana 和 Filebeat 两篇能接上。Kibana 需要稳定字段和 data view;Filebeat 需要知道输出到哪个 Logstash、用什么证书、出现背压时怎么处理。Logstash 如果没有明确合同,前后两端都会变成猜谜。
交付后还要做一次端到端回归:让 Filebeat 发送一条带唯一标识的测试日志,确认 Logstash 收到、字段被正确解析、Elasticsearch data stream 写入成功、Kibana 按时间范围能查到。这个回归样例要长期保留,用于证书轮换、API Key 轮换、模板变更和 Logstash 升级后的快速验证。
如果平台允许自动化,可以把这条回归做成脚本或运维任务,但文章这里不强行给脚本。关键不是脚本形式,而是每次变更都能证明链路仍然完整:采集、传输、解析、写入、查询五个步骤缺一不可。
从系列衔接看,Logstash 是 Elasticsearch 和 Filebeat 之间的契约层。上一篇已经定义了目标数据流、模板、权限和生命周期,本篇把原始日志转换成这些目标字段;后面的 Kibana 会基于这些字段做搜索、图表和告警,Filebeat 会负责把边缘主机日志稳定送进来。任何一端字段名、时间戳、证书或账号变更,都要回到这份管道合同里同步更新。只有这样,ELK 才不是四个工具临时拼在一起,而是一条可维护的数据链路。
如果团队后续引入 Dify 或其他自动化分析工具,也应读取 Logstash 已经稳定输出的字段,而不是再次解析原始 message。自动化越靠近结构化字段,结果越可解释;越依赖大模型猜日志格式,越难复盘。
这也是日志平台长期维护的关键:让机器消费字段,让人阅读原文,让失败路径留下证据,并把每次格式变化沉淀成样本、规则和验收记录。
管道交接时还要明确负责人,避免失败事件长期无人认领,也避免重复修同一个字段问题。
本篇结论
Logstash 的核心价值不是“插件多”,而是把复杂日志加工放到一个可测试、可审计、可回滚的位置。输入端要轻,过滤端要明确,输出端要安全,失败路径要能复盘。JSON 优先,grok 谨慎;PQ 解决短期缓冲,不替代备份;DLQ 留住失败证据,不替代修复流程。
下一篇进入 Kibana。只有 Logstash 把字段、时间、数据集和失败边界处理稳定,Kibana 的搜索、Lens、Dashboard 和告警才有可靠的数据基础。