从原理讲起,用大白话和大量可运行示例,带你把 Splunk 从“装上都懵”用到“日志分析一把好手”。覆盖架构、数据接入、SPL 搜索语言、仪表盘、告警、实战场景与集群进阶。
在这个“一切都在产生日志”的时代,服务器、防火墙、手机 App、IoT 设备每秒都在吐出海量机器数据。谁能从这些杂乱的数据里快速找到“哪里出问题了”“谁在攻击我”“用户喜欢什么”,谁就掌握了主动权。Splunk 就是做这件事的“机器数据搜索引擎”。
本教程的定位是:不假设你有任何 Splunk 基础,但会认真把底层原理讲清楚,让你不只是会点按钮,而是真正理解“为什么这么配、为什么这么查”。每章都配有通俗比喻、可复制的命令、以及图解,读完后你应当能够独立部署一套 Splunk、接入真实日志、写出实用的搜索与告警、并做出像样的仪表盘。
内容地图(五大模块):
阅读建议:先把前 3 章的原理读通,再跟着第 4-6 章动手装一套环境(免费许可证足够练手),然后大量练习第 7-9 章的 SPL——这是 Splunk 的“内功”。最后用第 10-16 章把成果可视化、自动化、并走向生产级部署。
如果只能给 Splunk 下一个通俗的定义,我会这样说:Splunk 是一款专门用来收集、搜索、分析和可视化"机器数据"的软件平台。 所谓"机器数据",就是计算机、网络设备、手机 App、传感器等一切电子设备在运行时自动吐出来的原始记录——日志、指标、网络流量、配置文件变更……它们就像机器之间、机器与系统之间不停窃窃私语的"黑话"。
你可以把 Splunk 想象成一台"机器世界的同声传译机 + 超级搜索引擎"。机器说的话人类看不懂,Splunk 负责把它们翻译成可读、可检索、可画图的语言,让我们能回答诸如"昨天凌晨三点到底是谁把数据库删了?""这个接口为什么慢了十倍?""过去一周哪个地区的用户流失最严重?"这类问题。
官方对 Splunk 的定位是:它是一个用于运维可观测性(Observability)与安全分析(Security)的平台,能够把任意来源的机器数据转化为可供行动的洞察(actionable insights)。换句话说,它不只是"看日志的工具",更是把数据变成决策的引擎。
很多人一听到"机器数据"就以为是服务器日志。其实那只是冰山一角。机器数据的常见形态包括:
生活化比喻:如果一家公司是一栋大楼,那么机器数据就是这栋楼里所有摄像头、门禁、电表、对讲机 24 小时不间断产生的"现场记录"。平时没人看,但一旦出了事——失火、盗窃、电梯故障——把这些记录调出来,真相就藏在里面。Splunk 就是那套能把这些杂乱记录快速翻找、交叉比对、并给你画成图表的"大楼监控中枢"。
为什么偏偏是"机器"数据,而不是"人写的数据"?因为机器生成的数据有三个特点:体量巨大(一天几十 GB 很常见)、格式杂乱(有的像英文句子,有的像逗号分隔的表格,有的甚至是一堆看不懂的十六进制)、从不休息(7×24 小时持续产生)。人脑和 Excel 根本处理不了这种规模,必须用 Splunk 这样的"机器读机器"的工具。再强调一点:机器数据里其实藏着大量"人"的行为——每一次点击、支付、登录都是机器记录下来的,所以分析机器数据,最终常常是在分析"用户想干什么"。
Splunk 的价值,可以浓缩成三大类场景。
系统一出问题,传统做法是运维人员挨个登录服务器、用 grep 翻日志,慢且容易漏。Splunk 把所有日志集中在一起,一条 SPL(Splunk 查询语言)就能跨几百台机器秒级定位报错。比喻:以前是"挨家挨户敲门问有没有看见嫌疑犯",现在是"直接调出全市监控大屏"。举个具体例子:某电商大促时下单接口突然变慢,运维只需搜 index=web uri="/order*" | timechart avg(response_time),几秒就能看到是哪个机房、哪个时间窗开始变慢,而不必一台台登机器。
黑客攻击、内部违规、账号被盗,都会在日志里留下痕迹。Splunk 可以实时关联异常登录、权限变更、数据外发等行为,触发告警。它在安全领域常用作 SIEM(安全信息与事件管理)底座,帮企业满足等保、审计等合规要求。例如,一条典型的安全检测可以这样写:index=auth failed OR "invalid" | stats count by user, src_ip | where count > 20,用来揪出"短时间内被暴力破解的账号"。这种把离散事件聚合成可疑模式的能力,正是 Splunk 安全场景的杀手锏。
机器数据不止关于机器,也关于人。每一次点击、下单、登录都是一条事件。Splunk 能分析用户行为、转化漏斗、区域分布,为业务决策提供数据支撑。一家电商可以通过 Splunk 发现"加购未支付的订单在支付页停留超过 8 秒会大量流失",从而优化页面。再比如运营想知道"哪个渠道带来的付费用户最多",只需把推广链接参数、注册日志、支付日志三张"借书卡"按用户 ID 关联起来,就能画出渠道转化漏斗——这已经不是在排障,而是直接用机器数据做增长决策了。
Splunk 旗下产品不少,初学者最容易混淆的是下面几款:
一句话区分:Enterprise 是你自己买服务器自己管,Cloud 是租 Splunk 的云服务。两者的查询语言、核心概念完全一致,本教程的内容对两者都适用。
ELK 是另一套流行的日志分析栈(Elasticsearch + Logstash + Kibana)。很多初学者会问"学 Splunk 还是 ELK?"下面这张表帮你快速建立认知(注意:ELK 自 2019 年起已并入 Elastic 公司,部分组件如 Beats 替代了 Logstash 的采集角色)。
| 对比维度 | Splunk(Enterprise / Cloud) | ELK(Elastic Stack) |
|---|---|---|
| 商业模式 | 商业闭源,按数据量(GB/天)授权收费 | 开源为主,Elastic 提供商业订阅(部分高级功能收费) |
| 查询语言 | SPL,语法接近 Unix 管道,学习曲线平缓 | Query DSL(JSON 风格)或 KQL,较偏底层 |
| 上手难度 | 开箱即用,界面与文档成熟 | 组件多、需自行拼装与调优 |
| 生态与可视化 | Dashboard、告警、知识对象体系完善 | Kibana 可视化强,但联动告警偏弱 |
| 运维成本 | Enterprise 需自运维集群;Cloud 免运维 | 通常需自运维 Elasticsearch 集群,资源消耗大 |
| 适用场景 | 企业级安全、合规、复杂运维分析 | 成本敏感、技术团队强的日志检索 |
比喻:Splunk 像一台"品牌一体机",插电就能用但贵;ELK 像"自己攒的组装电脑",便宜灵活但得自己会装。两者底层目标相同——把机器数据变成洞察。
这一章我们建立了对 Splunk 的"第一印象":它是机器数据的翻译官与搜索引擎;它解决排障、安全、业务洞察三大类问题;它主要分为自托管的 Enterprise 与云端的 Cloud;它和 ELK 是同一赛道的两类选手。
光看不练是学不会 Splunk 的。对个人学习者,最友好的路径是:去 Splunk 官网下载 Splunk Enterprise 免费版(提供有限数据量的免费_license,足够学习),在一台闲置电脑或虚拟机上装好,再用自带的"Search & Reporting"应用导入一份示例数据(如 _internal 索引里的自身运行日志)练手。官方还提供了 Splunk Free 与各类动手实验(Splunk Search Tutorial 数据集),跟着敲一遍第 2 章的 SPL,你会对"事件、字段、索引"产生肌肉记忆。记住:Splunk 的门槛不在概念,而在"亲手搜过一百次"之后形成的直觉。
为了把相对抽象的概念讲透,本章全程使用一个比喻:把 Splunk 想象成一座超级图书馆,SPL 就是你向图书管理员提问的语言。下面逐个拆解每个核心概念,并配可运行的 SPL 示例。
在 Splunk 中,事件(event)是数据的最小单位,通常对应日志里的一行(或按时间戳聚合的一段)。每个事件都带有一个时间戳和若干属性。
比喻:一条事件就像图书馆里的一张"借书记录卡"——上面写着"谁(host)、在什么时间(time)、借了什么书(raw 内容)"。Splunk 搜索,本质上就是在一大堆借书卡里翻找你要的那几张。
# 示例 1:查看某个索引里最近的事件(默认返回前 100 条)
index=main
# 示例 2:只找状态码为 404 的网页访问事件
index=web status=404
索引(index)是 Splunk 存储数据的逻辑容器,也是数据落地到磁盘后按"倒排索引 + 压缩原始数据"组织的数据库。你可以建多个索引,例如 web、security、db,分别存放不同来源的数据。
比喻:索引就像图书馆里"文学区""科技区""少儿区"等不同书库。你借书时会先说"我要去科技区找",Splunk 搜索时也得先指定 index=xxx,这样它就不必翻遍整座馆,速度更快、计费更准。
索引为什么查得快?秘密在于它底层用了倒排索引(inverted index):传统方式是一本本书去翻"有没有出现某个词",而倒排索引提前建好了一张"词 → 出现在哪几页"的地图。当你搜 status=404,Splunk 直接查这张地图,瞬间定位到相关事件,而不是从头扫描所有数据。同时,Splunk 还把原始数据压缩存储,既省空间又能随时回看原文。理解"索引=分库 + 倒排地图",你就懂了 Splunk 高性能的根本来源。
# 示例 3:统计 web 索引里各状态码出现的次数
index=web | stats count by status
每一条事件都会被打上三个重要的元数据标签,帮助你知道它"从哪来":
/var/log/nginx/access.log。access_combined、syslog。它告诉 Splunk 该用哪套规则去解析(比如时间戳长什么样)。web-01。比喻:source 是"这本书摆在几号书架",sourcetype 是"这本书是小说还是说明书(决定你怎么读它)",host 是"这本书来自哪个分馆"。这三个标签让你能精确定位"哪台机器的哪个日志文件的哪种格式出了问题"。
# 示例 4:只看来自 web-01 这台主机的 Nginx 访问日志
index=web host=web-01 sourcetype=access_combined
字段是事件里被提取出来的"键值对",例如 status=200、user=alice、bytes=1234。Splunk 默认会自动提取很多字段,你也能用 rex、eval 自己造字段。时间戳(_time)是每条事件的专属时间,是做时间过滤和趋势图的基础。
比喻:字段就像借书卡上预先印好的"填空格"——姓名、书名、日期。Splunk 之所以比 grep 强,就在于它认识的不是"一堆文字",而是"一堆带名字的值",这样你才能问"把所有状态为 200 的记录按月画成曲线"。
# 示例 5:用 rex 从原始文本里提取客户端 IP,并统计访问最多的 IP
index=web | rex field=_raw "(?<client_ip>\d+\.\d+\.\d+\.\d+)" | top client_ip
原始数据(_raw)是事件最原始的一整行文本,字段都是从它里面解析出来的。即使你的字段提取规则写错了,原始数据依然原封不动地保存在 Splunk 里——你随时可以重新解析。这是 Splunk 设计上的一大优点:先全量存储,后按需解析。
比喻:_raw 就是借书卡背后粘着的"原始小票",字段是从小票上抄下来的要点。小票永远在,抄错了可以重抄。
知识对象是你在使用 Splunk 过程中创建、并能被反复复用的"智慧资产",包括:字段提取规则(Field extractions)、事件类型(Event types)、标签(Tags)、查找表(Lookups)、报表(Reports)、仪表盘(Dashboards)、告警(Alerts)等。
比喻:如果事件是借书卡、索引是书库,那么知识对象就是图书管理员积累的"秘籍"——"凡是标题含'黑客'的卡片,自动打上红色标签""每小时自动统计一次逾期记录并邮件提醒我"。它们让 Splunk 从"被动查询"升级为"主动洞察"。
# 示例 6:按小时统计过去 24 小时的访问量(用 eval 造出 hour 字段)
index=web earliest=-24h
| eval hour=strftime(_time, "%H")
| stats count by hour
当你往索引里写数据时,Splunk 会把数据按时间切成固定大小的桶(bucket),每个 bucket 是一个包含原始数据(压缩)和索引文件的目录。bucket 会随"年龄"在 hot → warm → cold → frozen 四个状态间迁移:刚写入是热的(hot),逐渐冷却,最终可被归档或删除(frozen)。
比喻:bucket 就像图书馆按"年份 + 批次"排列的抽屉。新书先放"热抽屉"(随时翻),放久了挪到"冷抽屉"(少翻但还在),实在太旧就归档入库。这种分层让 Splunk 既能快速写入新数据,又能低成本保留历史数据。
至此,我们已经用"图书馆 + 搜索引擎"的比喻,把事件、索引、源/源类型/主机、字段、时间戳、原始数据、知识对象、bucket 串成了一条完整的认知线。下一章,我们把镜头拉远,看看这些"图书"到底是怎么从四面八方汇集、又被谁管理的。
掌握概念后,最关键的是学会"怎么问"。SPL 的精髓是管道(pipe):把上一步的结果用 | 传给下一步,像流水线一样逐步加工。下面再补几条高频实用句式,配合前文的示例,足以应付 80% 的日常查询:
# 示例 7:按天统计错误数趋势(自动按 _time 天粒度聚合)
index=web status>=500 | timechart count
# 示例 8:用 eval 计算平均响应时间,再按接口分组
index=web | stats avg(response_time) as avg_rt by uri | sort -avg_rt
# 示例 9:过滤 + 统计 + 排序,找出访问量 Top 10 的 URL
index=web | top 10 uri
小窍门:不确定数据里有哪些字段,先裸跑 index=xxx | head 20 看几条样本;不确定字段名,点开搜索结果左侧的"感兴趣字段"列表挑。Splunk 的交互式探索,比"先想好完整语句再执行"更高效。
单机模式(Standalone):所有功能(采集、索引、搜索)都跑在一台 Splunk 实例上。适合学习、PoC 或数据量很小的场景。比喻:一家只有一名管理员的小社区图书馆,借书、登记、查目录全由他一人包办。
分布式模式(Distributed Deployment):把不同职责拆分到不同机器——转发器负责采集、索引器负责存、搜索头负责查、再加上一堆"管理组件"统筹全局。比喻:一家连锁图书馆总馆,有专门的采编部(转发器)、藏书部(索引器)、阅览查询台(搜索头),还有馆长办公室(各种管理节点)。当数据量上了规模,分布式是必然选择。
那"多大算大、该不该上分布式"?一个粗略的参考线:单台索引器通常能从容处理每天几百 GB 的写入;当单台机器 CPU/磁盘顶不住,或你要求"一台挂了数据不能丢、查询不能断",就该拆分布式。拆分带来的好处是横向扩展与高可用,代价则是多出了部署服务器、许可证主节点等"管理开销"。初学者先用单机把数据管线跑通,理解 Input→Parsing→Indexing→Search 每一段在哪台机器发生,再谈扩展,才不会在众多组件里迷路。
通用转发器(Universal Forwarder,UF):极轻量的客户端,几乎只做一件事——把原始数据原封不动地"搬运"到下游(索引器或重型转发器)。它不解析、不索引,资源占用极小,可以装成百上千台业务机上。比喻:只负责把成捆的借书卡从分馆送到总馆的"快递员",路上不看内容。
重型转发器(Heavy Forwarder,HF):一台完整的 Splunk Enterprise 实例,开启了转发功能。它能在发送前解析、过滤、改写、路由数据——比如丢弃无用日志、给数据打标签、或按来源分流到不同索引。比喻:一位"会分拣的快递主管",发货前先拆包检查、归类、贴标签,再发往不同仓库。代价是它更重、更耗资源。
经验法则:绝大多数采集场景用 UF 就够;只有需要在"入库前"做复杂处理(如脱敏、过滤 90% 噪声)时,才上 HF。
| 维度 | 通用转发器 UF | 重型转发器 HF |
|---|---|---|
| 本质 | 轻量专用采集客户端 | 完整 Splunk Enterprise + 转发功能 |
| 能否解析/索引 | 否,仅转发原始数据 | 能解析、过滤、改写、路由 |
| 资源占用 | 极低(MB 级内存) | 较高(接近一台索引器) |
| 典型部署量 | 成百上千台业务机 | 少数几台边缘/网关节点 |
| 许可证 | 免费 | 需 Enterprise 许可证 |
索引器(Indexer)是分布式架构里的核心处理组件。它负责执行数据管线中的数据解析(parsing)与索引(indexing)两段,把原始数据流变成可搜索的事件,并落到磁盘的 bucket 里。生产环境通常部署多个索引器组成集群,既分摊压力又冗余容灾。
索引器干活时可以拆成几条内部"小流水线":先解析(parsing)决定事件边界与时间戳,再合并(merging)把零散行拼成完整事件,然后类型化(typing)做字段提取与索引写入。平时你不必关心这三步,但排障时如果某类数据"时间戳全错"或"被切成碎行",就知道问题多半出在解析段——这时要去检查 sourcetype 的 props.conf 时间戳规则。一句话:索引器是"把杂乱原材料加工成可检索知识"的工厂车间。
搜索头(Search Head)是用户直接打交道的地方。你在这里敲 SPL、看仪表盘、设告警。它本身不存数据,而是把搜索请求分发给索引器,再把结果汇总、渲染给你。多个搜索头可以组成"搜索头集群(SHC)"以实现高可用与负载均衡。比喻:它是图书馆的"咨询台",你不进书库,而是把问题告诉咨询台,由它派人去各书库取书、再综合答复你。
搜索头还有一个关键职责:保管所有知识对象。你在搜索头创建的仪表盘、字段提取、告警,默认都存这里(集群模式下由 Deployer 统一下发到各搜索头,保证大家"看到的书目一致")。这也意味着:知识对象要建在搜索头上,而不是索引器上——很多新手误以为"提取字段的配置要改索引器",其实它属于搜索管理层。
下面这张手绘风示意图展示了典型分布式部署中,数据从产生到被查询的完整流向,并标注了各组件在"数据管线"中的角色。
针对不同规模,给出可落地的拓扑(从小到大):
通用建议:先把"采集(UF)→ 索引(Indexer)→ 搜索(Search Head)"这条主干跑通,再按需补管理组件;许可证主节点务必尽早规划,避免某天突然"超额停写"造成数据断档。
第 1 章提到 Splunk 把数据变成可搜索事件,本章的组件正是为这套数据管线(Data Pipeline)服务的。官方将其分为四段,对应三个处理层(tiers):
值得一提:官方文档有时把 Parsing 与 Indexing 合称为"索引过程(indexing process)",但排障或规划容量时最好把它们拆开看——解析段吃 CPU,索引段吃磁盘 I/O,瓶颈在哪决定了你该加 CPU 还是加磁盘。这种"按段落定位瓶颈"的思维,是 Splunk 运维进阶的关键。
记住这条主线,你就能理解为什么"转发器不索引、索引器不直面用户、搜索头不存数据"——它们是数据管线不同段落的分工者。至此,第 1–3 章全部打通:你已知道 Splunk 是什么、核心概念怎么用、以及数据究竟如何从源头流到你的查询窗口。
main,导致权限、 retention(保留期)、计费都无法区分。正确做法:按业务/敏感度建独立索引。把这五个坑记在心里,你部署 Splunk 时的"翻车率"会大幅下降。下一阶段(后续章节)我们将动手做一次完整的"数据接入 → 解析 → 建仪表盘"实战,把本章的组件与管线真正跑起来。
如果把 Splunk 比作一位专门帮你看管机器日志的"超级管家",那安装就是帮他搬进新家、办好入住手续。这一章我们就手把手把 Splunk Enterprise(企业版)这位管家请到 Linux 和 Windows 两台"房子"里,并教会你怎么开关门、怎么登录他的工作台,以及——最重要的——免费版到底能让他干多少活。
在动手安装前,先搞清楚你要请的是哪位"管家",免得装错了版本白忙活。Splunk 家族主要有这么几位:
打个比方:Enterprise 是"大管家本人",UF 是"跑腿小弟",HF 是"会初步分拣的跑腿小弟",Cloud 是"请别人帮你养的管家"。
管家也挑环境。官方推荐的最低配置大致是:4 核 CPU、至少 4GB 内存(实际生产建议 12GB 以上)、足够的磁盘空间(日志会越攒越多)。系统支持主流的 Linux(Ubuntu、CentOS/RHEL、Debian 等)、Windows Server,以及 macOS(仅适合本地体验,不建议当生产)。
最重要的是记住几个"门牌号"(端口),后面配置和排错都要用到:
| 端口 | 用途 | 说明 |
|---|---|---|
| 8000 | Web 界面(Splunk Web) | 你用浏览器访问 http://服务器IP:8000 就是它 |
| 8089 | 管理端口(splunkd 管理) | CLI 命令、部署服务器通信都走它 |
| 9997 | 转发接收端口(receiving) | 索引器"收件箱",UF 把日志发到这里 |
| 514 | Syslog 网络端口 | 网络设备发 syslog 常用(TCP/UDP) |
| 8088 | HEC(HTTP Event Collector)端口 | 用 HTTP 方式写日志的入口 |
⚠️ 提前提醒:如果本机已经跑了占用 8000、8089 等端口的程序(比如另一个 Splunk、Tomcat、某些监控代理),安装或启动就会"撞车"。装之前最好用 netstat -tlnp | grep 8000 之类查一下。
Debian 系发行版(Ubuntu、Debian)用 .deb 安装包。假设你已经从 splunk.com 下载好了文件 splunk-9.2.0-...-linux-2.6-amd64.deb,放在 /tmp 下。
# 1) 用 dpkg 安装,默认会解压到 /opt/splunk
sudo dpkg -i /tmp/splunk-9.2.0-linux-2.6-amd64.deb
# 2) 进入安装目录(SPLUNK_HOME 就是这里)
cd /opt/splunk
# 3) 第一次启动,会让你看许可协议,按空格翻页、输入 "y" 同意
sudo ./bin/splunk start
# 更省事的一步到位:直接带参数同意协议并设管理员密码
sudo ./bin/splunk start --accept-license --answer-yes \
--no-prompt --seed-passwd '你的强密码123'
启动成功后,Splunk 会提示你访问 http://127.0.0.1:8000。此时它默认以 root 运行(不推荐生产环境),生产建议创建专用用户 splunk 并把目录属主改给它。
Red Hat 系发行版用 .rpm 包,命令换成 rpm 或 yum/dnf:
# 1) 用 rpm 安装,同样默认装到 /opt/splunk
sudo rpm -i /tmp/splunk-9.2.0-linux-2.6-x86_64.rpm
# 2) 启动并接受协议
cd /opt/splunk
sudo ./bin/splunk start --accept-license --answer-yes --no-prompt \
--seed-passwd '你的强密码123'
小提示:CentOS 7 默认开了 firewalld,要让别人能访问 8000 端口,记得放行:
sudo firewall-cmd --permanent --add-port=8000/tcp
sudo firewall-cmd --reload
Windows 上最简单:双击官方下载的 splunk-9.2.0-...-x64-release.msi(或 .exe)启动图形化安装向导。
C:\Program Files\Splunk\(这就是 Windows 下的 $SPLUNK_HOME)。装完后在"开始菜单 → Splunk → Splunk Enterprise"即可启动;命令行则在 PowerShell / CMD 里进入安装目录:
cd "C:\Program Files\Splunk\bin"
.\splunk.exe start
Splunk 的命令行都在 $SPLUNK_HOME/bin/ 下,核心命令就是 splunk(Windows 是 splunk.exe)。常用动作如下:
| 目的 | Linux 命令 | Windows 命令 |
|---|---|---|
| 启动 | ./splunk start | .\splunk.exe start |
| 停止(温和) | ./splunk stop | .\splunk.exe stop |
| 重启 | ./splunk restart | .\splunk.exe restart |
| 查看运行状态 | ./splunk status | .\splunk.exe status |
| 开机自启(Linux) | ./splunk enable boot-start | (安装时勾选服务即可) |
| 关闭自启 | ./splunk disable boot-start | 在服务里禁用 |
如果命令需要管理员权限操作本机服务或端口,记得 Linux 前面加 sudo。改了配置后多数情况用 restart 让它重新加载最稳妥。
启动后打开浏览器,访问 http://<服务器IP或本机>:8000。第一次会看到登录页,用刚才设置的 admin 账号登录。进去后默认是"搜索与报表"(Search & Reporting)首页——这就是你以后和这位管家"对话"(写 SPL 搜索语句)的主舞台。
试用 60 天后,企业许可证过期,Splunk 会拒绝索引新数据。这时可以切到免费许可证(Splunk Free),继续白嫖,代价是:
切换方法(在 Web 界面):
设置(Settings) → 许可证(Licensing) → 更改许可证组(Change license group)
→ 选择 "Free"(免费) → 确认重启
也可以通过 CLI 查看当前许可证状态:
./splunk list licenses # 查看已安装许可证
./splunk edit license-group FREE # 命令行切换为免费组(重启生效)
💡 经验之谈:500MB/天 对个人学习、小服务器日志完全够用。如果哪天超了,Splunk 只是当天停止索引新数据,不会删你历史数据,第二天配额刷新后又会自动恢复。所以学习阶段随便折腾,历史数据很安全。
netstat -tlnp | grep 8000 找到占用的进程,关掉它或给 Splunk 换端口:./splunk set web-port 8800 然后重启。chown -R splunk:splunk /opt/splunk 并以 splunk 用户运行。$SPLUNK_HOME/var/lib/splunk 所在分区有足够空间,并规划好第 6 章会讲的 bucket 保留策略。./splunk cmd splunkd rest --noauth /services/admin/users/admin -X DELETE 删掉再重建,或更稳妥地参考官方"重置 admin 密码"文档。setenforce 0,生产则应写正确的 SELinux 策略而非直接关闭。装完别急着走,先"体检"一下确认管家真的上岗了:
./splunk version # 查看版本,确认二进制正常
./splunk status # 应显示 "splunkd is running"
./splunk show splunkd-port # 查看 splunkd 管理端口(默认8089)
./splunk health report # 运行健康检查(较新版本支持)
再用浏览器访问 http://服务器IP:8000,能出现登录页即 Web 服务正常。如果本机能开、别处打不开,问题几乎都在防火墙/安全组(见 4.9)。
把容易混淆的几个"身份"一次性理清楚,避免部署时选错:
| 身份 | 每天索引上限 | 是否解析数据 | 典型用途 |
|---|---|---|---|
| Enterprise(试用) | 无限制(60天) | 是(本身是索引器) | 正式评估/生产 |
| Enterprise(Free 组) | 500MB | 是 | 个人学习/小环境 |
| Universal Forwarder | 不索引(只转发) | 否 | 装在各业务机收日志 |
| Heavy Forwarder | 不索引(可转发) | 是(转发前解析) | 边界脱敏/预处理 |
| Splunk Cloud | 按订阅 | 云端托管 | 不想运维基础设施 |
关键点再强调一遍:UF 不索引也不解析,它只是"搬运工";真正"吃数据、建索引"的是索引器(Enterprise/Cloud)。所以 500MB/天 的限制,是算在索引器头上,不是每台 UF 各算 500MB。
splunk 用户并 chown -R splunk:splunk /opt/splunk。$SPLUNK_HOME/etc/(所有配置)和关键索引的 bucket 目录。到此,你的"数据管家"就算正式入住并办好手续了。下一章,我们教他怎么把家里(以及全公司)散落各处的日志"收"进来。
装好 Splunk 只是第一步,真正有价值的是"数据"。Splunk 有个外号叫"机器数据的引擎"——只要是机器吐出来的文本(日志、指标、配置、命令行输出……),它几乎都能吃。这一章我们讲清楚:数据有哪些"进门方式",以及最经典的转发器实战。
把数据"接"进 Splunk,主流有三条路,打个比方:
| 方式 | 生活化比喻 | 适用场景 |
|---|---|---|
| ① 监控文件/目录 | 管家守在打印机旁,一有新纸就拿走归档 | 本地/远程服务器的日志文件(最常见) |
| ② 网络端口(TCP/UDP) | 管家在门口放个收件箱,别人直接丢进来 | 网络设备 syslog、应用主动推送 |
| ③ HTTP Event Collector(HEC) | 管家开个 API 投稿通道,App 用代码直接投递 | 云原生、容器、自定义应用埋点 |
此外还有脚本化输入(Scripted Input):让 Splunk 定时跑一段脚本(比如调用 API 拉数据),把脚本的标准输出当成日志来索引——相当于管家每隔几分钟自己出门取一趟报纸。
这是 90% 场景的首选。Splunk 会"盯"住一个文件或整个目录,文件新增内容时它像 tail -f 一样持续读取。配置写进 inputs.conf:
# $SPLUNK_HOME/etc/system/local/inputs.conf
[monitor:///var/log]
# 监控整个 /var/log 目录下的新日志
index = main
sourcetype = linux_logs
host = web-server-01
[monitor:///var/log/nginx/access.log]
# 只监控 Nginx 访问日志这一份文件
index = web
sourcetype = nginx:access
[monitor:///opt/app/logs/*.log]
# 通配符:监控该目录下所有 .log 文件(不含子目录)
index = app
sourcetype = myapp
用 CLI 让 Splunk 立刻开始监控也行(命令会自动写进 inputs.conf):
./splunk add monitor /var/log -index main -sourcetype linux_logs
./splunk add monitor /var/log/nginx/access.log -index web
几个实用参数:_TCP_ROUTING 决定发给哪个索引器组;disabled = true 可临时停用某个监控而不删配置;whitelist/blacklist 用正则筛文件。
让 Splunk 自己开一个"收件箱端口",网络设备或应用直接把日志发过来。常见的是 syslog(UDP 514 或 TCP 514/1514)。
# $SPLUNK_HOME/etc/system/local/inputs.conf
# 监听 TCP 514 端口接收 syslog
[tcp://:514]
index = network
sourcetype = syslog
connection_host = ip # 用对端 IP 作为 host 字段
# 监听 UDP 514(老式设备常用,但不保证送达)
[udp://:514]
index = network
sourcetype = syslog
命令行等价写法:
./splunk add tcp 514 -index network -sourcetype syslog
./splunk add udp 514 -index network -sourcetype syslog
⚠️ 生产环境 Linux 默认不允许非 root 程序绑定 1024 以下端口,所以常改用 1514、5140 等高端口,再用 iptables/firewalld 做端口转发到 514。
HEC 是给开发者准备的"投稿 API":你的应用程序用一段 HTTP 请求,把 JSON 格式的事件直接 POST 给 Splunk,无需安装任何转发器。非常适合容器、Kubernetes、Serverless 等云原生场景。
启用步骤(Web 界面):设置 → 数据输入 → HTTP Event Collector → 新建令牌(New Token),给令牌起名、选索引,生成一串类似 EA8F...长字符串 的 token。
然后用 curl 投递一条事件:
curl -k https://splunk-host:8088/services/collector/event \
-H "Authorization: Splunk EA8F8C3D-..." \
-d '{"event":"用户登录成功", "sourcetype":"myapp", "index":"web", "host":"app-01"}'
对应的 inputs.conf 里 HEC 由 [http] 与 [http://<token名>] 段落控制(通常由 Web 向导自动生成,无需手写)。HEC 是"已解析后直接写索引"的捷径,性能好、对应用友好。
现实世界里,日志分散在几十上百台机器上,你不可能给每台都装整套 Enterprise。正确姿势是:每台产日志的机器装个轻量的 Universal Forwarder(UF),由它把日志送到中央的索引器(Indexer)。UF 就像遍布各楼层的"收件小弟",中央索引器才是"大管家"。
完整五步实战(假设索引器 IP 是 192.168.1.10,监听 9997):
在日志机器上下载对应平台的 UF 安装包并安装(Linux .deb/.rpm、Windows .msi)。装完路径同样是 $SPLUNK_HOME(UF 默认 /opt/splunkforwarder)。
在索引器上开启接收端口 9997:
# 在索引器上执行
./splunk enable listen 9997 -auth admin:密码
outputs.conf 决定 UF 把数据发往哪个索引器。写在 UF 的 $SPLUNK_HOME/etc/system/local/outputs.conf:
# outputs.conf(在转发器上)
[tcpout]
defaultGroup = my_indexers # 默认发往下面这个组
[tcpout:my_indexers]
server = 192.168.1.10:9997 # 索引器地址:接收端口,多个用逗号隔开
# server = idx1:9997,idx2:9997 # 可配置多个做负载均衡/高可用
useACK = true # 开启确认,防止数据丢失(推荐)
用 CLI 一行搞定(等价于上面):
./splunk add forward-server 192.168.1.10:9997 -auth admin:密码
还是在 UF 上,告诉它监控哪些日志:
# inputs.conf(在转发器上)
[monitor:///var/log]
index = main
sourcetype = linux_logs
[monitor:///var/log/nginx/access.log]
index = web
sourcetype = nginx:access
CLI 等价:
./splunk add monitor /var/log -index main -sourcetype linux_logs
./splunk restart
重启后,UF 会建立到索引器 9997 的连接,开始把 /var/log 的新内容源源不断送到中央索引器。到索引器上搜索 index=main sourcetype=linux_logs 就能看到数据了。
💡 进阶:如果机器很多,可以再部署一台 Deployment Server(部署服务器),用 deploymentclient.conf 让所有 UF 自动从它拉取统一的 inputs.conf / outputs.conf,实现"一处改、千台同步"。
| 文件 | 段落 | 关键参数 | 含义 |
|---|---|---|---|
| inputs.conf | [monitor://路径] | index / sourcetype / host | 目标索引 / 源类型 / 主机名 |
| inputs.conf | 同上 | _TCP_ROUTING | 指定发往 outputs.conf 里的哪个组 |
| inputs.conf | 同上 | disabled | true 表示停用该输入 |
| inputs.conf | 同上 | whitelist / blacklist | 用正则筛选监控哪些文件 |
| outputs.conf | [tcpout:组名] | server | 索引器地址:端口,多值逗号分隔 |
| outputs.conf | [tcpout] | defaultGroup | 默认使用哪个 tcpout 组 |
| outputs.conf | 同上 | useACK | 开启索引器确认,防丢数据 |
不想碰配置文件?Splunk Web 提供了图形化"添加数据(Add Data)"向导,非常适合初学者:
新手先用向导跑通"感觉",熟练后再回到配置文件,效率最高。
# 监控 Nginx 访问日志
[monitor:///var/log/nginx/access.log]
index = web
sourcetype = nginx:access
# 监控 Apache 访问与错误日志
[monitor:///var/log/apache2/access.log]
index = web
sourcetype = access_combined
[monitor:///var/log/apache2/error.log]
index = web
sourcetype = linux_error
接入后可用 SPL 一键看"今天访问量最高的 IP":
index=web sourcetype=nginx:access
| stats count by clientip
| sort - count
| head 10
# Linux:监控系统日志与安全日志
[monitor:///var/log]
index = os
sourcetype = linux_logs
# Windows:监控三大事件日志(需装 Windows 版 UF)
[WinEventLog://Application]
index = os
[WinEventLog://System]
index = os
[WinEventLog://Security]
index = os
查"最近登录失败":
index=os sourcetype=linux_logs "Failed password"
| top user
有些数据不在日志文件里,而需要"主动去问"——比如调一个 API 拉取云账单、查数据库当前连接数。这时用脚本化输入:Splunk 按设定间隔执行一段脚本(Shell/Python/PowerShell 都行),把脚本打印到标准输出(stdout)的内容当成日志来索引。
# inputs.conf 配置脚本输入
[script:///opt/splunk/etc/system/local/scripts/check_conns.sh]
interval = 60 # 每 60 秒跑一次
index = os
sourcetype = conn_check
disabled = false
# /opt/splunk/etc/system/local/scripts/check_conns.sh 示例
#!/bin/bash
echo "$(date '+%Y-%m-%d %H:%M:%S') current_connections=$(ss -tn | wc -l)"
这样一来,Splunk 每隔一分钟就"出门取一趟报纸",把当前连接数记下来,后续就能画成趋势图。
接数据最常遇到的问题就是"配了半天搜不到"。按这个顺序查,基本都能定位:
ls -l /var/log/nginx/access.log 确认。nc -zv 192.168.1.10 9997;UF 上 ./splunk list forward-server 看是否显示 "active"。./splunk show listen 应列出 9997。index=xxx,新手常忘写索引导致"啥都没有"。| metadata type=sourcetypes 看实际进来的源类型名。# 在索引器上实时看"原始数据有没有到"(类似抓包)
./splunk search 'index=* | head 5' # 抽查近期事件
./splunk list forward-server -auth admin:密码 # UF 上确认转发目标 active
HEC 的本质是"拿着令牌(token)的 HTTP 投稿通道"。一个令牌对应一份配置(默认索引、默认 sourcetype、是否允许覆盖)。事件体是 JSON,最关键的两个字段:
event:事件正文,可以是字符串,也可以是结构化 JSON 对象。time(可选):Unix 时间戳,不填就用到达时间。更复杂的批量投递(一次发多条):
curl -k https://splunk-host:8088/services/collector/event \
-H "Authorization: Splunk 你的TOKEN" \
-d '{"event":{"user":"alice","action":"login"},"sourcetype":"myapp","index":"web"}'
若返回 {"text":"Success","code":0} 即投递成功。HEC 走的是 8088 端口,记得在索引器(或 Heavy Forwarder)上开启 HEC 并允许该端口;同时 HEC 默认就直接写索引,绕过了 UF 的转发链路,适合云上应用直连。
到此,数据已经从四面八方"进门"了。但你知道 Splunk 在背后如何把一条杂乱的原始日志,变成能在毫秒级被搜索的结构化事件吗?这正是下一章要揭开的"厨房内幕"。
前面你只管"把数据丢进门",却没看清门后面发生了什么。这一章是进阶的关键:搞懂 Splunk 处理一条日志的内部旅程,你才能解释"为什么有的字段提取不生效""为什么转发器上配的解析没起作用""为什么搜索有时候卡"。我们把它拆成"原料 → 切菜 → 炒菜 → 装罐封存"几个阶段。
Splunk 官方把索引期(index-time)的数据处理,归为两大管道(pipeline):parsing(解析管道) 和 indexing(索引管道)。但真正干活时,它们又被细分成一串带"队列(queue)"的小车间。可以想象一条工厂流水线:
parsing(切事件、认时间)、merging(同源聚合)、typing(按源类型提取字段)。这是"理解数据"的核心。各车间之间用队列(queue)衔接——队列就像"暂存传送带",前一道工序忙不过来时,数据先排队,避免整条线崩掉。主要队列有:parsingQueue(输入→解析之间)、aggQueue(合并阶段之后、typing 之前)、indexQueue(typing→索引之间)。
假设 /var/log/nginx/access.log 新追加了一行。Splunk 的 tailreading 组件像 tail -f 一样发现新增内容,把连续字节流按约 64KB 切成大块(chunk),推入 parsingQueue。此时数据还是"没感情的原始字节",Splunk 还不知道哪里是一行、哪行是一个事件。
数据进入 parsing 子车道,这里做三件大事:
LINE_BREAKER 规则,把大块字节切成一条条事件(event)。一个事件≈日志里的一"行"(但多行堆栈日志会被智能合并成一条)。TIME_FORMAT/TIME_PREFIX 等规则从事件文本里揪出时间,转成 Splunk 内部统一的时间。认不出就用"数据到达时间"兜底。host(哪台机器)、source(哪个文件)、sourcetype(什么类型)这三张"身份证",并确定字符集编码。切好的零散事件,按同一来源重新聚合成更大的数据块,方便后续批量处理。这一步之后数据进入 aggQueue。聚合的意义在于:来自同一个文件的事件被打包在一起,保证顺序和批处理效率。
数据从 aggQueue 进入 typing 子车道,这是最"个性化"的一步——它根据 sourcetype 应用对应规则:
transforms.conf / props.conf 里定义的字段提取(比如用正则抓出 clientip、method、status)。_TCP_ROUTING 或 queue 改写)。处理完的事件推入 indexQueue,准备落盘。
最后一道车间。Splunk 把事件拆成可搜索的小片段(segment),构建倒排索引,然后把原始数据压缩成 rawdata 日志文件,连同索引文件(tsidx 时间序列索引、各类元数据)一起写入磁盘上的 bucket(桶)。写完后还会做"索引后压缩(post-indexing compression)"。至此,这条日志从"一堆字节"变成了"秒级可搜的结构化记录"。
下面这张 SVG 流程图把上面的阶段和队列画出来(横轴就是数据流动方向):
这是新手最容易踩的坑,务必记住:Universal Forwarder(UF)默认只读取数据、切块,然后把原始字节直接转发给索引器,它不做解析(不切事件、不认时间、不提取字段)。解析这道"理解数据"的重活,只在索引器(Indexer)或重型转发器(Heavy Forwarder, HF)上发生。
为什么这么设计?两个原因:
props.conf 字段提取规则,压根不会执行。📌 所以记住铁律:想做字段提取、数据改写、路由,配置写在索引器(或 HF)端,别写在 UF 的 props.conf/transforms.conf 里。UF 上通常只写 inputs.conf(收什么)和 outputs.conf(发哪去)。
唯一的例外是 HF(重型转发器):它本质是"带解析能力的转发器",会在转发之前就执行解析管道,适合在把数据送出企业边界前先做脱敏、过滤、聚合,减轻索引器压力。
解析完的数据不是随便堆在硬盘上,而是被分装进一个个桶(bucket)。每个 bucket 是磁盘上的一组目录与文件,包含两样核心东西:
一个索引(index)由许多 bucket 组成的集合构成,按时间顺序和大小自动切分成一个个桶。每个桶内部的数据被限制在一段有限的时间范围内——就像把一年的报纸按月塞进不同的档案盒。
桶会随着年龄和大小,像"轮岗"一样在几个状态间滚动(roll):
| 状态 | 说明 | 能否搜索 | 触发滚动条件 |
|---|---|---|---|
| Hot(热桶) | 正在被写入新数据;一个索引可同时有多个热桶开放 | ✅ 可搜 | 达到特定大小 或 索引器重启 → 滚到温 |
| Warm(温桶) | 不再写入,但留在原目录(与热桶同区);数量可能很多 | ✅ 可搜 | 索引温桶数达上限 → 最旧的滚到冷 |
| Cold(冷桶) | 按年龄移到另一位置(常配到更便宜的大容量磁盘) | ✅ 可搜 | 达到时间/大小阈值 → 滚到冻结 |
| Frozen(冻结) | 默认被删除;也可配置先归档到外部存储 | ❌ 删除后不可搜(归档除外) | — |
| Thawed(解冻) | 从归档恢复的桶,回到索引供搜索 | ✅ 可搜 | 手动解冻归档数据 |
一句话记忆:热桶在写 → 写满滚温 → 温老滚冷 → 冷到期限冻结(删除或归档)→ 归档的还能解冻救回来。
默认目录结构大致是:
$SPLUNK_HOME/var/lib/splunk/<索引名>/
├── db/ # 热桶 + 温桶(hot/warm)
├── colddb/ # 冷桶(cold)
└── thaweddb/ # 解冻回来的桶(thawed)
这些滚动规则(热桶多大、最多留几个温桶、冷桶保留多久)都由 indexes.conf 控制。比如:
# indexes.conf 片段:配置某索引的保留策略
[myindex]
homePath = $SPLUNK_DB/myindex/db
coldPath = $SPLUNK_DB/myindex/colddb
thawedPath = $SPLUNK_DB/myindex/thaweddb
maxDataSize = auto # 热桶到约 750MB 就滚到温
maxWarmDBCount = 300 # 温桶最多 300 个,超了 oldest 滚冷
frozenTimePeriodInSecs = 189216000 # 约 6 年后冻结(删除/归档)
💡 排错小贴士:如果你发现"搜索突然搜不到很老的数据",八成是冷桶已经滚到冻结被删了;如果"磁盘涨得太快",去调 frozenTimePeriodInSecs 或把冷桶指到廉价存储。而 parsingQueue/aggQueue/indexQueue 一旦长期接近满(可用 ./splunk cmd splunkd status 或 DMC 监控面板看),说明某道工序是瓶颈,得加索引器或优化解析。
第 ④ 步提到的"按 sourcetype 提取字段",背后是两份配置文件。以 Nginx 访问日志为例,我们想在 Typing 阶段自动抓出 clientip、method、status 三个字段:
# props.conf(写在索引器/HF 端)
[nginx:access]
# 告诉 Splunk 用下面的 REPORT 规则提取字段
REPORT-nginx-fields = nginx_extract
# transforms.conf(同端)
[nginx_extract]
REGEX = ^(?\d+\.\d+\.\d+\.\d+)\s\S+\s\S+\s\[[^\]]+\]\s"(?\w+)\s[^"]*"\s(?\d{3})
FORMAT = clientip::$1 method::$2 status::$3
⚠️ 再次强调:这套提取规则必须写在真正执行解析的那一端(索引器或 HF)。如果你误装在 UF 上,UF 不会解析,规则等于白写——这正是 6.4 节铁律的实例。
另外,props.conf 里还能控制 Parsing 阶段的行为,比如:
[nginx:access]
LINE_BREAKER = ([\r\n]+) # 怎么算"一行/一个事件"
TIME_PREFIX = \[ # 时间戳前面的标记
TIME_FORMAT = %d/%b/%Y:%H:%M:%S %z
MAX_TIMESTAMP_LOOKAHEAD = 32 # 往前最多看多少字符找时间
TRUNCATE = 10000 # 单条事件超过此长度则截断
回到流程图里的三个队列(parsingQueue、aggQueue、indexQueue)。它们的大小(默认约 1.5MB~数 MB)决定了整条流水线的"缓冲能力"。当某一道工序成了瓶颈:
props.conf 的字段提取,或在索引器上增加并行管道(pipeline set):server.conf 里设 parallelIngestionPipelines = 2 让 Splunk 同时跑两套流水线。maxQueueSize 增大缓冲。监控这些队列,可用 分布式管理控制台(DMC) 的 "Indexing Performance" 面板,或命令行 ./splunk cmd splunkd status 粗略查看。调优口诀:先找最满的那个队列,它就是瓶颈所在。
两点补充,帮助你对管道有更完整的认知:
props.conf 的 TRUNCATE,默认 10000 字节左右,可上调)。超长的"巨无霸"事件会被截断,所以解析超大行(如超长 JSON 一行)时记得调大该值,否则数据"看起来进来却缺尾巴"。回顾这条"日志的奇幻漂流":
理解了这套流水线,你在第 7 章往后写搜索(SPL)、做字段提取、排查"为什么数据没进来/搜不到"时,就会像看自己家厨房一样胸有成竹。下一章,我们正式走进 SPL 搜索语言的世界。
在 Splunk 体系里,一切分析都从 SPL(Search Processing Language,搜索处理语言) 开始。很多初学者把 SPL 当成"在日志里搜关键词",这是最大的误解。准确地说,一次 SPL 搜索由两个阶段组成:
把这两件事分开理解非常关键:过滤阶段决定了"数据对不对",转换阶段决定了"答案好不好看"。Splunk 之所以强大,是因为它把这两个阶段用一条灵活的管道(pipeline)串了起来——你可以像搭积木一样,把任意多个处理步骤连起来。
| 的思想:和 Linux 管道一模一样
SPL 的灵魂是竖线符号 |(管道符)。它的含义和 Linux 命令行里的 | 完全相同:把左边命令的输出,当作右边命令的输入。左边不管产出什么,右边只管接着处理,彼此解耦。
举个例子,对比一下你就秒懂:
# Linux 管道:找出含 "error" 的行,再统计行数
cat app.log | grep "error" | wc -l
# SPL 管道:找出含 "error" 的事件,再统计数量
index=web "error" | stats count
在 Linux 里,grep 的输出是一行行文本,喂给 wc -l;在 SPL 里,index=web "error" 的输出是一批事件(每件事包含若干字段),喂给 stats count。管道思想的红利是:你可以无限串联——先过滤、再统计、再排序、再画表,每一步只关心自己那一小块逻辑。
Splunk 搜索栏下方有一个模式(Mode)下拉框,默认是智能模式(Smart Mode)。三种模式的区别,本质在于 Splunk "偷偷"帮你做了多少自动转换:
| 模式 | 行为 | 适用场景 |
|---|---|---|
| 详细模式(Verbose) | 返回所有字段、应用所有高亮和字段提取,不做任何省略 | 调试字段提取、确认数据细节时 |
| 智能模式(Smart,默认) | 根据命令自动决定返回哪些字段,兼顾性能与信息量 | 绝大多数日常搜索 |
| 快速模式(Fast) | 只返回搜索命令明确用到的字段,速度快但信息少 | 只要聚合数字、不关心原始字段时 |
实战建议:刚开始学习时,建议切到详细模式,这样你能看到事件里到底有哪些字段、字段值长什么样,写 SPL 时才心里有数。等熟练了再用智能/快速模式提速。
Splunk 是时间序列数据库,几乎每次搜索都必须指定时间范围。搜索栏右上方的时间选择器是最常用的入口:最近 15 分钟、最近 24 小时、最近 7 天,或自定义时间段。你也可以直接在 SPL 里用 earliest= 和 latest= 写死时间,覆盖界面选择:
# 只看最近 1 小时的数据
index=web earliest=-1h
# 看昨天一整天(相对时间写法)
index=web earliest=-1d@d latest=@d
# 看 2026-08-20 全天(绝对时间写法)
index=web earliest=08/20/2026:00:00:00 latest=08/21/2026:00:00:00
时间写法小抄:-1h = 1 小时前,@d = 对齐到当天零点,@m = 对齐到整点。熟练使用 earliest/latest 能避免"忘记选时间导致搜不到数据"的尴尬。
index=xxx sourcetype=yyy "关键词"任何 SPL 的第一段(管道符之前的部分)都叫搜索谓词(search predicate),它的任务是过滤事件。最经典的骨架是:
index=web sourcetype=access_combined "error"
解释:index=web 限定只查名为 web 的索引;sourcetype=access_combined 进一步限定数据来源类型(通常是某种日志格式);"error" 是自由文本关键词,要求原始事件里包含单词 error。三者是默认 AND 关系——必须同时满足。
# 搜索多个关键词(空格 = AND)
index=web sourcetype=access_combined "failed login"
注意:带引号的 "failed login" 表示短语搜索,要求这两个词紧挨着且顺序一致;如果只写 failed login 不加引号,则表示这两个词都出现即可(位置不限)。
字段名=值 精准定位
Splunk 在索引时会自动(或在配置后)从日志里提取出字段(field),比如 status(HTTP 状态码)、clientip(客户端 IP)、method(请求方法)。一旦有了字段,你就可以像查数据库一样精确过滤,而不必依赖模糊的关键词:
# 只看状态码为 200 的成功请求
index=web status=200
字段搜索比关键词搜索快得多,也更可靠——它不会把"status=200"误匹配到正文里某个无关的 200。这是 SPL 与"纯文本 grep"的分水岭。
当过滤条件变复杂,就需要布尔运算符组合。Splunk 支持 AND、OR、NOT 三种,但有两个极易踩坑的注意点:
and、or 不会报错,但会被当成普通关键词去搜文本,结果完全不对。一定要写 AND、OR、NOT。NOT > AND > OR。当混合使用时,强烈建议用括号把逻辑分组写清楚,避免歧义。# 状态是 200 或 304(带缓存的成功响应)
index=web (status=200 OR status=304)
# 状态 200 且请求方法是 GET
index=web status=200 AND method=GET
# 排除非成功响应(注意 NOT 大写,且建议加括号)
index=web NOT status=200
# 混合逻辑:成功响应,或者是来自内网的请求
index=web (status=200 OR status=304) AND NOT clientip="10.*"
小贴士:方括号 [ ] 在 SPL 里表示子搜索(subsearch),和括号 ( ) 的分组作用完全不同,别混淆。
结合大量排障经验,下面这些错误几乎人人都会犯一次,提前避坑能省下大把时间:
and/or/not 不会报错,却会被当成文本去搜,结果"看起来有数据但其实逻辑全错"。务必大写。index=web status>100 是在原始事件上过滤(字段必须是数字类型才正确);... | where count>100 是在统计结果上过滤。两者阶段不同,不能互换。*error* 会让 Splunk 无法利用索引前缀优化,全索引扫描极慢。能用字段搜索就别用模糊文本。|stats 和 | stats 都行,但 || 双竖线不是 SPL 语法,会直接报错。
理解执行顺序,能让你写出更快的搜索。Splunk 把管道分成两类命令:流式命令(streaming)可以逐事件并行处理,速度极快,例如 where、eval、rex、fields;非流式/阻塞命令(non-streaming)必须等前面全部结果到位才能算,例如 stats、transaction、join、sort。经验法则:尽量把过滤(index 谓词)和流式命令放在前面,把 stats 这类重命令放在最后,这样需要搬运和落地的数据量最小,搜索头负担最轻。一个反例是先用 head 1000000 再过滤——头命令把大量数据传下去,反而更慢;应该先用索引谓词过滤,再取少量样本。
*:模糊匹配的利器
Splunk 支持用 * 表示"任意字符序列",在字段名、字段值、sourcetype、index 名上都能用:
# 匹配所有以 access_ 开头的 sourcetype(如 access_combined、access_common)
index=web sourcetype=access_*
# 状态码以 4 开头(所有客户端错误:400/401/403/404...)
index=web status=4*
# 任意值,常用于"确认字段存在"
index=web status=*
性能提醒:通配符 * 用在值的中间或开头(如 *error*、*200)会显著降低搜索速度,因为它无法利用索引的前缀优化。尽量把 * 放在末尾(前缀匹配)。
写复杂搜索前,先确认数据长什么样,是好习惯。两个最顺手的"探路"命令:
# 只看前 20 条事件,快速预览原始日志
index=web | head 20
# 统计出现次数最多的状态码,并按次数降序
index=web | top status
# 只看出现最多的前 5 个 uri 路径
index=web | top limit=5 uri
# 看最后 10 条(和 head 相反,从头跳过只看尾部)
index=web | tail 10
top 默认会额外给出 count(出现次数)和 percent(占比)两列,帮你一眼看清数据分布;limit=N 可控制返回前几名。
下面的示意图展示了一条典型 SPL 的"数据流动":原始事件从索引流出,依次经过过滤、提取、统计、排序,最终变成一张小表格。
本章进入 SPL 的"肌肉区"。我们会用真实日志场景,逐个拆解每天都会用到的命令,每个命令配 2~3 个可运行示例,并解释输出含义。假设我们有一份 Web 访问日志,索引名 index=web,sourcetype 为 access_combined,包含字段 _time、host、clientip、method、uri、status、bytes、useragent 等。
stats 是 SPL 里使用频率最高的"转换命令"。它把一批事件聚合成若干行汇总结果,通过 BY 子句决定"按什么分组"。核心语法:
stats (聚合函数(字段) [AS 新字段名])... [BY 分组字段]
常用聚合函数:count() 计数、sum() 求和、avg() 平均值、dc() 去重计数(distinct count)、list() 列出所有值(含重复)、values() 列出去重后的值。
# 示例 1:按状态码统计每种状态出现了多少次
index=web | stats count by status
输出含义:返回两列 status 和 count,每行代表一个状态码及其出现次数,例如 200→9500、404→320。一眼看出哪种响应最常见。
# 示例 2:按主机 + 状态码二维分组,看每个主机的健康度
index=web | stats count by host, status
输出含义:BY 后可以跟多个字段,用逗号分隔。结果会是 host × status 的组合行,比如 web01/200、web01/404、web02/200……适合横向对比多台机器。
# 示例 3:按主机统计流量总和与平均值
index=web | stats sum(bytes) AS 总流量, avg(bytes) AS 平均流量 by host
输出含义:使用 AS 给计算结果起中文/易读别名。sum(bytes) 是每台主机下发的总字节数,avg(bytes) 是平均每次请求的大小。用逗号可在一个 stats 里同时算多个指标。
# 示例 4:用 dc() 统计每台主机的独立访客数(去重计数)
index=web | stats dc(clientip) AS 独立访客数 by host
输出含义:dc() 只数不重复的 clientip,比 count 更能反映"真实人数"。这是做 UV(Unique Visitor)统计的标准写法。
# 示例 5:用 list/values 看某个访客都请求了哪些状态码
index=web | stats list(status) AS 状态序列 by clientip
index=web | stats values(method) AS 用过的方法 by clientip
输出含义:list() 保留全部出现顺序和重复(如 200,200,404),values() 自动去重(如 GET,POST)。两者产生多值字段,适合"看这个 IP 的行为画像"。
# 示例 6:在 stats 内部直接用 eval 做条件计数
index=web | stats count(eval(status>=400)) AS 错误数, count AS 总数 by host
输出含义:count(eval(...)) 只对满足表达式的事件计数,于是无需先过滤就能同时得到"错误数"和"总数",便于后续算错误率。
stats 的两条隐藏规则务必记住:第一,BY 后面的分组字段不支持通配符,不能写 BY source* 想一次分多组,必须老老实实把字段名逐一列全;第二,聚合里可以嵌套 eval 表达式(如示例 6),这让"先算条件、再聚合"成为现实,是 stats 最灵活的特性之一。另外,当 BY 的字段本身是多值字段时,记得加 dedup_splitvals=true,否则每个值组合都会展开成多行,结果会被放大。
stats 给你一张静态汇总表,而 chart 和 timechart 把聚合结果画成图表。chart 是通用二维图表(X 轴是某个字段),timechart 的 X 轴固定是时间,最适合看趋势。
怎么选 chart 还是 timechart:当你想看"按某个非时间维度(如主机、状态码、地区)的分布",用 chart;当你想看"指标随时间如何变化"(如每小时错误数、每日活跃用户),用 timechart。二者都支持 BY 子句把一条线拆成多条,也都支持在聚合前用 bin 思路自动分桶(timechart 内部就自动调用了 bin)。一个常见组合是 timechart span=1h count by status:既能看总量趋势,又能拆开看每类状态码各自的曲线,是监控大盘的常客。注意 timechart 默认只保留 BY 字段里最多的前 10 个值(limit=top10),其余归入 OTHER,需要全量时加 limit=0。
# 示例 7:按状态码统计数量,生成分类柱状图
index=web | chart count by status
输出含义:X 轴是 status,Y 轴是数量,适合直观对比各状态码占比。等价于 stats count by status 但多了可视化。
# 示例 8:按主机统计平均响应大小
index=web | chart avg(bytes) by host
输出含义:每个 host 一个点/柱,高度是平均 bytes,快速发现"哪台机器响应体偏大"。
# 示例 9:按时间(每小时)统计错误事件数,看故障何时爆发
index=web status>=400 | timechart span=1h count
输出含义:span=1h 把时间切成每小时一个桶(bucket),count 统计每桶内事件数。输出是一条随时间起伏的曲线,错误突增的时间点一目了然。这是运维排障的标配。
# 示例 10:按天统计独立访客数(dc)
index=web | timechart span=1d dc(clientip) AS 每日独立访客
输出含义:X 轴是天,Y 轴是当天去重后的访客数,用于观察流量日活走势。
# 示例 11:按时间看各状态码的数量,自动分多条线
index=web | timechart span=1h count by status
输出含义:BY status 让 timechart 为每个状态码画一条独立曲线(200、404、500……),可同时监控多类响应的时间分布。配合 limit=top5 可只保留最多的 5 类,其余归入 OTHER。
eval 用来创建或修改字段,支持运算符和大量函数。语法:
eval 新字段=表达式 [, 另一个字段=表达式]...
下面逐个演示最实用的函数。
eval 的运算符与类型小抄:eval 支持算术(+ - * / %)、比较(= != < > <= >=)、逻辑(AND OR NOT)和字符串拼接(.,注意是点号不是加号)。一个极易踩的坑是类型:Splunk 从日志里提取的字段大多是"字符串",哪怕看起来像数字。所以 eval a = status + 1 可能报类型错或做字符串拼接,正确做法是先用 tonumber(status) 转成数字。同理,字符串比较区分大小写,需要忽略大小写时先 lower() 再比。当一条 eval 里要算多个字段,用逗号分隔即可,且后一个表达式可以引用前一个刚生成的字段,这是做多步派生的关键技巧。
# 示例 12:if 函数——二选一。状态码 200 标记 OK,否则标记 Problem
index=web | eval 结果=if(status=200, "OK", "Problem") | table status 结果
输出含义:if(条件, 真值, 假值),逐事件判断。新增"结果"列,200 显示 OK,其余显示 Problem。
# 示例 13:case 函数——多分支,把状态码转成友好文字
index=web | eval 类别=case(
status<300, "成功",
status<400, "重定向",
status<500, "客户端错误",
true(), "服务端错误"
) | stats count by 类别
输出含义:case(条件1,值1,条件2,值2,...,默认) 按顺序匹配,第一个为真者生效;最后的 true() 作为兜底。这样就把冰冷的数字变成了"成功/重定向/客户端错误/服务端错误"四类,统计报表瞬间可读。
# 示例 14:match 函数——正则判断,识别移动端访问
index=web | eval 是否移动端=if(match(useragent, "Mobile"), "是", "否") | stats count by 是否移动端
输出含义:match(字段, "正则") 返回布尔值,配合 if 把含 "Mobile" 的 UA 标为移动端,从而统计移动/桌面流量占比。
# 示例 15:split 函数——按分隔符拆成多值字段
index=web | eval 段=split(uri, "/") | eval 第一段=lower(mvindex(段, 1)) | table uri 第一段
输出含义:split(uri,"/") 把 "/api/user" 拆成多值 ["", "api", "user"],再用 mvindex(段,1) 取第 2 个元素 "api",lower() 转小写。常用于从 URL 里抠出资源类型。
# 示例 16:substr / lower / upper / tonumber 字符串与类型转换
index=web | eval 前缀=substr(uri, 1, 5)
| eval 方法小写=lower(method)
| eval 方法大写=upper(method)
| eval 状态码数字=tonumber(status)
| table uri 前缀 方法小写 方法大写 状态码数字
输出含义:substr(字段,起,止) 截取子串;lower/upper 大小写转换(常用于统一大小写后再分组);tonumber() 把字符串型的 status 转成数字,方便做数值比较或运算(注意 Splunk 字段多为字符串,需要数字时记得转换)。
# 示例 17:where 过滤聚合后的结果(只保留数量 > 100 的状态码)
index=web | stats count by status | where count>100
输出含义:where 作用在已经统计出来的行上(与搜索阶段的 status>100 不同)。它过滤的是统计结果,而不是原始事件。常配合 stats 使用。
# 示例 18:sort 排序,负号表示降序
index=web | top status | sort -count
输出含义:sort -count 按 count 从大到小排(升序去掉负号)。top 本就降序,这里演示语法;若按多个字段排可写 sort -count +status。
# 示例 19:fields 只保留需要的字段(裁剪掉无关列)
index=web | fields _time status clientip uri
输出含义:fields 保留列出的字段、丢弃其余,让事件列表更清爽,也提升性能(快速模式下尤其有用)。
# 示例 20:table 把结果输出为指定列顺序的纯表格
index=web | table _time status clientip uri
输出含义:table 与 fields 的区别在于:table 强制按你给的列顺序只展示这些字段,常用于生成最终报表;fields 只是"裁剪",保留字段原有顺序。
# 示例 21:rename 给字段起人话名字
index=web | rename clientip AS "客户端IP", status AS "状态码", uri AS "请求路径"
输出含义:rename 旧名 AS 新名 把技术字段名换成业务可读名,导出给非技术同事看时非常友好(新名含空格要加引号)。
# 示例 22:top 找最频繁的 uri(默认降序,带 count/percent 列)
index=web | top limit=10 uri
输出含义:返回访问量最高的前 10 个页面路径,及各自次数和占比,适合做"热门页面排行"。
# 示例 23:rare 找最稀有的状态码(出现次数最少)
index=web | rare limit=5 status
输出含义:与 top 相反,rare 列出出现次数最少的几个值。常用来发现"异常但罕见"的情况,比如某种极少出现的 5xx 错误。
# 示例 24:top 按主机分组,再在各主机内找最多的 uri
index=web | top uri by host
输出含义:BY host 让 top 在每个 host 内部各自排序,结果里会多一个 host 列,便于对比不同机器的热门资源。
单个命令只是零件,真正的威力在组合。设想一个真实场景:线上 5xx 告警,你要快速定位"哪台机器、哪个 URL、什么时段"出的问题。一个成熟的分析链路是这样的:
# 完整排障链路:过滤 -> 提取 -> 分组统计 -> 排序 -> 取表
index=web status>=500
| rex field=_raw "(?<uri>\"(?:POST|GET) (\S+) HTTP)"
| stats count AS 错误数, dc(clientip) AS 影响用户数 by host, uri
| sort -错误数
| head 10
| rename host AS 主机, uri AS 路径, 错误数 AS 错误数, 影响用户数 AS 影响用户数
| table 主机 路径 错误数 影响用户数
链路解读:先用 status>=500 在索引层精准过滤(最快);rex 现场抠出 URL 路径(万一日志没自动提取);stats ... by host, uri 把错误按"机器×路径"二维聚合,同时算错误数和受影响独立用户数;sort -错误数 把最严重排前面;head 10 只取 Top10;最后 rename + table 输出一份干净报表直接发群。整条链路一气呵成,这就是 SPL 管道思想的精髓。
| 易混对 | 关键区别 |
|---|---|
fields vs table | fields 只裁剪字段、保留原有顺序与事件结构;table 强制按指定列顺序输出纯表格,更适合作最终报表。 |
sort vs top | top 自带计数与占比、自动降序且只返回前 N 名;sort 是对已有结果任意排序,不计数。 |
where vs 搜索谓词 | where 作用在转换后的结果行上;搜索谓词作用在原始事件上,且有索引加速。 |
stats vs eventstats | stats 把事件压缩成汇总行(行数变少);eventstats 保留每一行原始事件,只是额外追加聚合列。 |
记住这些区别,能避免"我明明写了 sort 为什么没分组""为什么 eventstats 之后行数没变"这类困惑。下一章我们会用更重的命令把这种组合推向高级玩法。
当你需要"从没被自动识别的日志里抠字段""把分散的事件串成一次完整会话""把 A 索引和 B 索引用关联补全",就要用到本章的高级命令。它们是区分"会用 SPL"与"精通 SPL"的分水岭。
很多日志的字段没有被自动提取,整行内容躺在 _raw 字段里。这时 rex 用正则表达式的命名捕获组,(在搜索时)把想要的内容提成一个新字段。语法:
rex [field=源字段] "正则(?<新字段名>捕获模式)..."
若不写 field=,默认对 _raw 提取。下面用一条典型 Apache 访问日志现场演示。
# 原始日志示例(_raw):
# 192.168.1.10 - - [20/Aug/2026:10:22:31 +0800] "GET /api/user?id=99 HTTP/1.1" 200 1234
# 示例 25:用 rex 一次性提取 客户端IP、请求方法、路径、状态码、字节数
index=web sourcetype=access_combined
| rex "(?<client_ip>\d+\.\d+\.\d+\.\d+).*?\"(?<method>\w+) (?<path>\S+) HTTP.*?\" (?<status>\d+) (?<bytes>\d+)"
| table client_ip method path status bytes
输出含义:正则里的 (?<client_ip>\d+\.\d+\.\d+\.\d+) 把 IP 提进 client_ip 字段;(?<method>\w+) 提取 GET/POST;(?<path>\S+) 提取到第一个空格为止的 URL 路径;(?<status>\d+) 和 (?<bytes>\d+) 分别提取状态码和字节数。此后这些字段就能像自带字段一样用于 stats、where 等。
# 示例 26:从 useragent 里提取浏览器内核(指定 field=)
index=web | rex field=useragent "(?<browser>Chrome|Firefox|Safari|Edge)" | top browser
输出含义:field=useragent 指定从 useragent 字段(而非 _raw)提取,捕获组 (?<browser>...) 命中 Chrome/Firefox 等之一,再用 top 统计浏览器份额。
# 示例 27:max_match 提取一处日志里出现的多个值(多值字段)
index=web | rex field=uri max_match=0 "(?<param>\w+=\w+)" | table uri param
输出含义:max_match=0 表示不限次数匹配,于是 URL 里的 id=99&from=mobile 会被全部捕获进多值字段 param。
下图直观展示:左边是一坨看不出结构的原始 _raw 文本,经过 rex 正则"照妖镜"后,右边裂变出结构化的独立字段。
日志里"一次完整业务"往往分散在多条事件里(比如:浏览→加购→下单→支付)。transaction 按某个/某些相同字段把相关事件打包成一条"事务",并自动算出持续时间 duration、事件数 eventcount 等。常用参数:
| 参数 | 作用 |
|---|---|
<field-list> | 按哪些字段的相同值分组(如 clientip、sessionId) |
maxspan=30m | 事务最早与最晚事件的最大时间跨度,超出的不合并 |
startswith= / endswith= | 用搜索表达式标记事务的"开始事件"和"结束事件" |
maxevents=N | 单个事务最多包含的事件数 |
# 示例 28:按客户端 IP 归并 30 分钟内的所有请求,看成一次访问会话
index=web | transaction clientip maxspan=30m
输出含义:同一 clientip 在 30 分钟内的请求被合并成一条事务,新增 duration(会话时长秒)、eventcount(请求数)。可据此找"停留最久""点击最多"的访客。
# 示例 29:用 startswith / endswith 精确界定"从浏览到下单"的完整流程
index=web | transaction clientip startswith="view" endswith="purchase" | where duration>0
输出含义:仅当某 clientip 的事件序列以含 "view" 的事件开始、以含 "purchase" 的事件结束时,才形成一个事务。where duration>0 排除空事务。非常适合分析"用户从看到买到"的转化路径与时长。
# 示例 30:按 sessionId 归并,限制最多 10 个事件、跨度 10 分钟
index=web | transaction sessionId maxspan=10m maxevents=10
输出含义:用服务端下发的 sessionId(比 IP 更准)归并,避免同一 NAT 下多人混淆;maxevents=10 防止异常长会话拖慢性能。
transaction 的性能红线:transaction 是典型的非流式、高内存消耗命令,它必须把同一分组的所有事件拉到同一搜索头节点上才能合并。当分组键基数巨大(例如按 clientip 归并全量日志)或 maxspan 设得很长时,内存和耗时都会爆炸,甚至触发搜索中止。实战铁律:先用索引谓词把时间范围和条件收窄,再 transaction;优先用 sessionId 这类低基数、语义明确的键;maxspan 和 maxevents 一定要设上限。如果只是为了"算每个用户的事件数",用 stats count by clientip 远比 transaction 便宜——transaction 真正不可替代的场景是"需要 duration、需要 startswith/endswith 界定边界"的会话分析。
有时答案分散在多个索引里,需要"关联"。《SQL 党》会本能写 join,但 SPL 里要谨慎。
# 示例 31:join 把供应商信息(另一索引)关联到主日志
index=web product_id=*
| join product_id [ search index=vendors | rename pid AS product_id | fields product_id vendor_name ]
| table product_id vendor_name
输出含义:主搜索按 product_id 出事件;子搜索 [ ... ] 提供右侧数据集,rename 把 vendors 里的 pid 对齐成 product_id 作为连接键。默认 type=inner,只保留能匹配上的事件。也可用 join type=left product_id [...] 做左外连接,保留主表全部事件。
join 的性能注意点(非常重要):
[subsearch] 限制),大表 join 会丢数据。stats 或 lookup 替代的,尽量别用 join。例如"按 IP 补部门"用 lookup 比 join 快得多(见 9.5)。append 或干脆合并搜索再 stats。# 示例 32:append 把两个索引的事件"上下拼接"后再统一统计
index=web status=200
| append [ search index=web status=404 ]
| stats count by status
输出含义:append 把子搜索结果追加到主结果后面(纵向合并,不是按字段关联),常用于"把多个索引/条件的事件汇到一起再做统一聚合"。和 join 不同,append 不要求连接键,只是简单堆叠。
# 示例 33:同一 clientip 只保留第一条事件
index=web | dedup clientip
输出含义:dedup clientip 对每个不同的 clientip 只保留首次出现的事件,其余丢弃。适合"看看今天都有哪些 IP 来过"而不关心次数。
# 示例 34:按 (clientip, uri) 组合去重,并保留完整事件
index=web | dedup clientip uri keepevents=true
输出含义:多字段去重,相同 (IP, 路径) 只留一条;keepevents=true(默认)保留原事件全部字段,若设 false 则只输出去重键字段。
dedup 的经典用法:一是"去重告警"——同一错误可能被同一根因反复触发成千上万次,dedup alert_id 后每条根因只看一次,避免被刷屏;二是"取首/末样本"——dedup 默认保留时间上最先出现的事件,配合 sort -_time 可改为保留最新,常用于"每个设备只留最近一次心跳"。注意 dedup 是流式命令、内存友好,但当去重键基数极大(如按 clientip 去重全量日志)时仍需先收窄时间范围,否则它会缓冲大量分组。
lookup 是做数据丰富(enrichment)的最佳方式:用一个 CSV 查找表(如 IP→部门、产品ID→名称)给每条事件补上额外字段。它比 join 快、可缓存、支持百万行。语法:
lookup <查找表名> <查找表字段> AS <事件字段> OUTPUTNEW <返回字段> AS <新事件字段>
# 示例 35:用 IP 段查找表给 clientip 补上所属部门
index=web | lookup ip_to_dept clientip OUTPUTNEW department AS 部门 | stats count by 部门
输出含义:查找表 ip_to_dept 含 clientip 与 department 两列;Splunk 用事件的 clientip 去查表,把匹配的 department 以"部门"写入事件,于是能直接按部门统计流量。OUTPUTNEW 表示若事件已有"部门"字段则不覆盖;用 OUTPUT 则会强制覆盖。
# 示例 36:多字段映射,事件字段名与查找表列名不同时用 AS 对齐
index=web | lookup product_catalog product_id AS sku OUTPUTNEW product_name AS 商品名, price AS 单价
输出含义:事件里的 sku 对应查找表的 product_id,查得后把 product_name、price 写成"商品名""单价"。多列返回用逗号分隔即可。
查找表(CSV)怎么来:在 Splunk Web 的"设置 → 查找 → 查找表文件"上传一个 CSV(首行是列名,如 clientip,department,city),再到"查找定义"里把它命名(如 ip_to_dept),之后搜索里就能直接用名字引用。进阶还可配置自动查找(automatic lookup):在 props.conf 里声明 LOOKUP-xxx = ip_to_dept clientip OUTPUT department,这样所有命中该 sourcetype 的搜索都自动补上 department 字段,用户根本无需写 lookup 命令。对于会频繁更新的维度表(如员工花名册),还能配置时区感知、最大匹配数、大小写忽略等参数精细控制。一句话:lookup 既是搜索命令,更是"把外部维度数据融进 Splunk"的基础设施。
记住一个判断标准:只给自己临时用、数据量小、试错阶段,用 rex 或 extract 在搜索时随手提;全团队都要用、格式固定、需要长期稳定,则在 props.conf/transforms.conf 里做永久提取。临时提取快但不持久,永久提取一劳永逸却需要管理员权限和测试,按场景取舍即可。
字段提取是把"原始文本"变成"可用字段"的根本。Splunk 提供两条路径:
对于 key=value 形式的日志(如 status=200 user=bob),Splunk 的 KV_MODE 能自动把键值对提成字段,无需任何配置,开箱即用。你也可以在搜索里用 extract 命令对某个字段强制做 KV 提取:
# 示例 37:对 message 字段里 "k=v" 形式的内容做自动键值提取
index=app | extract kvdelim="=" pairdelim="," source=message
输出含义:extract 把 message 字段中形如 level=info,user=bob 的内容,按 = 分键值、按 , 分键值对,提取出 level、user 字段。
对于结构固定、量大、需要全员复用的提取,应在索引器/搜索头上做永久配置,这样所有用户搜出来的字段都自带,无需每次写 rex。核心是两个 stanza:
# props.conf:针对某 sourcetype 声明一个 REPORT 命名的提取
[access_combined]
REPORT-extract-fields = my_field_extraction
# transforms.conf:定义正则提取规则
[my_field_extraction]
REGEX = (?<client_ip>\d+\.\d+\.\d+\.\d+).*?"(?<method>\w+) (?<path>\S+).*?" (?<status>\d+)
FORMAT = client_ip::$1 method::$2 path::$3 status::$4
# 另一种写法:EXTRACT 直接在 props.conf 里内联正则
[access_combined]
EXTRACT- status = " (?<status>\d{3}) "
REPORT vs EXTRACT 区别:EXTRACT- 在 props.conf 里直接写正则,简单快捷;REPORT- 引用 transforms.conf 里的命名规则,可复用、可结合更复杂逻辑(如多步提取、查表)。EXTRACT/REPORT 属搜索时(search-time)提取,不影响已索引数据,改了立即对新搜索生效,是首推方案;索引时提取(INDEXED_EXTRACTIONS)则写入索引、查询更快但配置成本高。
Splunk 的"知识对象(Knowledge Objects)"是把常用逻辑沉淀成可复用资产的机制。掌握它们,你能把一长串 SPL 浓缩成一句。
| 知识对象 | 一句话说明 | 典型用法 |
|---|---|---|
| 字段(Field) | 事件上的键值属性 | status=200 |
| 字段别名(Field alias) | 给同一字段多个名字,兼容不同来源 | 把 clientip 和 c_ip 都指向 ip |
| 计算字段(Calculated field) | 在配置里定义 eval 表达式,自动产出 | 自动给每条事件算 is_error = if(status>=400,"y","n") |
| 标签(Tag) | 给字段值打业务标签,便于检索 | 把 status=500 标为 tag=严重故障,搜索 tag=严重故障 |
| 事件类型(Event type) | 把一组过滤条件命名成一个类型 | 定义 web_error = index=web status>=400,搜 eventtype=web_error |
| 宏(Macro) | 把一段 SPL 定义成可传参的"函数" | 定义 topn(1) 宏 = | top limit::$num$,调用 `topn(10)` |
# 示例 38:使用事件类型 + 标签,让搜索极简
eventtype=web_error tag=严重故障 | timechart count by host
# 示例 39:使用宏,把"按 X 取前 N"封装复用
index=web `topn(20)` uri
输出含义:事件类型 web_error 背后是一整串过滤条件,标签 严重故障 把零散的状态码语义化,宏 `topn(20)` 展开成 | top limit=20。三者结合,让团队共用同一套"业务语言",新人也能一眼读懂搜索意图。
正则写得好不好,直接决定 rex 提取的成败。这 5 条能帮你少走弯路:
(?<名字>...):Splunk 用 PCRE 语法,字段名写在 <> 里,名字只能含字母数字下划线,不能和已有字段冲突(冲突会被覆盖)。.*? 而非 .* 做"惰性"跳板:两个捕获组之间的填充内容用 .*?(尽可能少匹配),避免贪婪的 .* 把中间所有内容一口吞掉,导致后面的组匹配错位。\S+ 匹配"不含空格的一段":URL 路径、方法通常到空格为止,\S+(非空白字符)比 .+ 更精确。"、.、/、( 在正则里有特殊含义,字面匹配要加反斜杠,例如匹配引号写 \",匹配点写 \.。这是一个高频决策点,记住一句话:凡是"用一个小表去给大表补字段"的场景,优先用 lookup;只有真正需要"按复杂条件把两个大结果集做集合运算"时才用 join。 原因很直白:lookup 在后台被优化成哈希表查找,百万行查找表也能毫秒级命中,且结果可缓存、可随 Splunk 集群分发;join 则是把子搜索结果拉到搜索头再做嵌套循环式匹配,受行数/内存上限束缚,稍大就超时或丢数据。常见"用 IP 查部门""用产品 ID 查名称""用用户名查邮箱"都是 lookup 的教科书场景,把它固化成查找表后,全公司所有人搜出来的事件都自动带标签,远比每人各自写一遍 join 来得优雅。
新手用 SPL 解决一次问题,高手把解法沉淀成知识对象,让整个团队受益。一个成熟的 Splunk 环境,应当有这样的分工:SRE 把"web_error""db_slow"定义成事件类型,安全同事把攻击特征定义成标签,数据工程师把"is_error = if(status>=400,"y","n")"这类常用派生字段定义成计算字段(在 props.conf 里 EVAL-is_error = if(status>=400,"y","n")),于是所有人无需写 eval 就直接有 is_error 可用;把"clientip / c_ip / src_ip"统一成字段别名,避免同一含义三套名字;把"取 Top N""按小时分桶"封装成宏。当这些对象就位,一条原本 30 行的复杂搜索,可能就浓缩成 eventtype=web_error tag=严重故障 | `topn(20)` uri。这不仅是省打字,更是团队知识和排障经验的固化与传承。
# 示例 40:bin 手动分桶,再 stats(chart/timechart 内部其实自动调用了 bin)
index=web | bin _time span=1h | stats count by _time, host
输出含义:bin _time span=1h 把连续时间切成每小时一个桶(字段值变成桶起点),再 stats count by _time 得到每小时计数。功能等价于 timechart span=1h count by host,但 bin 让你能在分桶后做更自由的组合统计。
# 示例 41:streamstats 做"流式"累积/排名(保留原始每一行)
index=web | sort host _time | streamstats count AS 序号, avg(bytes) AS 滚动均值 window=5 by host
输出含义:streamstats 与 stats 不同——它不压缩行数,而是给每一行事件追加聚合值。window=5 表示只看"当前事件及前 4 个"做平均;by host 让每个主机独立计算。常用于"每行都带上'到目前为止的累计数/最近 N 条均值'"。
# 示例 42:eventstats 给每行补上"全局/分组"汇总值
index=web | eventstats avg(bytes) AS 全局平均字节 by host | eval 偏离=bytes-全局平均字节
输出含义:eventstats 同样保留全部原始行,但它在整个结果集(或 BY 分组)上算聚合,再把结果写回每一行。这里给每条请求算出"相对本机平均字节数的偏离",方便找异常大/小的响应。
# 示例 43:fillnull 把空值补成默认值,避免统计/图表出现空洞
index=web | fillnull value="未知" department | stats count by department
输出含义:没有部门字段的事件,fillnull 会把 department 填成"未知",于是统计时这些事件归到"未知"组而不是被悄悄丢弃,报表更完整。
# 示例 44(可选):geom 把 IP/坐标关联到地理信息做地图可视化
index=web | lookup geo_attr_clientip clientip OUTPUT continent, country
| geom geo_countries allFeatures=true featureIdField=country
输出含义:先用查找表把 clientip 映射到国家,再用 geom 关联内置地理边界(geo_countries),把国家字段映射成几何图形,配合地图可视化画出"访问来源全球分布"。属于 SPL 与地理空间结合的进阶玩法。
| 命令 | 核心作用 | 典型场景 |
|---|---|---|
| rex | 正则提取字段 | 从 _raw 抠 IP/路径/状态码 |
| transaction | 多事件合并成会话 | 用户转化路径、会话时长 |
| join | 按键关联两数据集 | 跨索引补全信息(慎用) |
| append | 纵向拼接结果 | 多条件/多索引汇总结算 |
| dedup | 按字段去重 | 只看"有哪些"不关心次数 |
| lookup | 查找表丰富 | IP→部门、ID→名称 |
| extract | KV 自动提取 | key=value 日志 |
| bin | 连续值离散分桶 | 自定义时间/数值分组 |
| streamstats / eventstats | 流式/全局聚合并保留原行 | 累计、排名、偏离度 |
| fillnull | 空值补默认值 | 报表完整性 |
| geom | 地理关联可视化 | 访问来源地图 |
学完这三章,你已经具备独立解决 80% 运维、安全、业务分析问题的能力。建议按这条路线持续精进:第一步,固化肌肉记忆——把第 8 章的 stats/eval/timechart 组合在真实数据上反复敲,直到不看文档也能写;第二步,建立字段观——遇到没提取的字段,先想"该用 rex 临时提,还是配 EXTRACT/REPORT 永久提",后者能惠及全员;第三步,追求性能——凡是大数据量搜索,先想"能不能在索引层过滤、能不能用 lookup 替代 join、transaction 的 maxspan 设了没";第四步,沉淀资产——把高频搜索抽象成事件类型、标签、计算字段和宏,让团队站在你的肩膀上。SPL 不难,难在"写得对、写得快、写得可被复用",而这恰恰就是高手与新手的分水岭。
至此,第 7~9 章已覆盖 SPL 从"会搜"到"精通"的完整路径:理解管道与搜索本质 → 掌握 stats/chart/eval/where 等日常利器 → 用 rex/transaction/join/lookup 做高级字段提取与关联,并学会用知识对象把经验沉淀复用。下一步,建议把这些命令在你的真实数据上逐个跑一遍,改改参数、换换字段,手感自然就来了。
在前面的章节里,我们已经能用 SPL 把一堆杂乱的原始日志变成干净的统计结果。但问题来了:你总不能每次都打开搜索框、敲一段 SPL、盯着一堆表格数字看吧?老板要的是"一眼看懂",运维要的是"大屏一眼扫过去就知道哪里炸了"。这一章,我们就来解决"把结果变成图、把图拼成盘"的问题。
仪表盘本质上是一个"结果容器 + 展示外壳"。它把多条搜索(Search)固化下来,按照你想要的布局排布成一块块面板(Panel),再各自选择合适的图表类型渲染。它解决了三件事:
在 Splunk 的体系里,这两个词必须分清楚:
| 概念 | 英文 | 一句话解释 | 类比 |
|---|---|---|---|
| 仪表盘 | Dashboard | 一个完整的可视化页面,有标题、有布局 | 一面监控墙 |
| 面板 | Panel | 仪表盘里的一个"格子",装一个搜索结果和一种图表 | 墙上的一块屏幕 |
| 行 | Row | 面板横向排列的容器,一行可以放多个面板 | 屏幕的一排 |
| 搜索 | Search | 面板背后真正干活的 SPL 查询 | 屏幕背后的信号线 |
补充一点:在经典(Classic)仪表盘里,底层是用 Simple XML 描述的;而新一代的 Dashboard Studio 则使用 JSON 描述(我们 10.10 会讲)。无论哪种,"Dashboard 由多个 Panel 组成、Panel 由 Search 驱动"这个骨架是不变的。
很多新手会卡在一个认知误区:以为"做仪表盘"是前端工程师的活,要会写代码。其实完全不是。Splunk 的设计哲学是"分析人员自己把分析固化成视图",所以绝大多数仪表盘靠鼠标拖拽就能完成(见 10.8)。只有在两种情况下你才需要碰 XML:一是要做可版本管理、可批量复制的工程化交付;二是要实现拖拽编辑器做不到的高级交互(比如复杂的 token 联动、条件显隐)。所以请放心,这一章你只要看懂结构、会用编辑器,就已经够用了,XML 是锦上添花而非门槛。
还有一个工程实践要提前说:仪表盘不是越多越好。一个组织里最怕的是"仪表盘爆炸"——每个人随手存几个,半年后谁也不知道哪个还在用、哪个数据对不对。建议按"角色 + 场景"来组织,比如"运维值班大屏""安全 SOC 视图""业务日报",每个仪表盘聚焦一类受众,命名规范统一,定期清理僵尸面板。这和你写代码要分模块是一个道理。
下面这张示意图把"仪表盘 → 行 → 面板 → 搜索/图表"的层级画了出来,建议先记住这张图,后面看 XML 就会非常轻松:
图 10-1:仪表盘的层级结构。注意最底层每个面板都绑定一条搜索,搜索产出数据,面板选择图表类型把数据画出来。
新手最顺手的路径是"先有搜索,再有盘"。假设你已经写好一条搜索:
index=web sourcetype=access_combined
| timechart span=1h count by status
在搜索结果页右上角点 Save As → Dashboard Panel,Splunk 会让你选择:新建仪表盘还是加入已有仪表盘、面板标题、放在哪一行、用哪种可视化。保存之后,这条搜索就被固化进仪表盘,以后打开仪表盘它会自动重跑。
Report 和 Dashboard Panel 有啥区别? 在 Splunk 里,"Report(报表)"是一条被保存的搜索 + 它的展示方式,它本身可以独立存在、可以定时跑、可以发邮件;而"Dashboard Panel"是把一个搜索(或引用一个 Report)嵌进仪表盘的一块格子。简单说:Report 是" lone wolf(独行侠)",Dashboard Panel 是"团队一员"。最佳实践是先建 Report,再在仪表盘里选 "Add Panel from Report",这样搜索逻辑集中维护。我见过太多团队把同一段 SPL 复制到十个面板里,后来改一个过滤条件要改十处还漏了三处——这就是没用好 Report 复用的代价。
另外提醒:仪表盘面板默认跑的是"实时查询当前时间窗",如果面板多、数据量大,打开一个仪表盘可能同时发起几十条搜索,对搜索头压力不小。生产大屏建议给面板设置合理的时间范围(比如固定 -24h)和自动刷新间隔(比如 5 分钟),并在 Saved Search 层面把结果缓存好,避免每次打开都全量重算。
更"工程化"的做法是先到 Settings → Searches, reports, and alerts 里把常用搜索保存成"Report(报表)",再在仪表盘里引用它。这样搜索逻辑只维护一份,仪表盘和告警都能复用,避免到处复制 SPL 导致改一处漏十处。
Splunk 提供了丰富的可视化类型,下面这张表把最常用的列出来,并告诉你"背后的数据应该长什么样":
| 可视化类型 | 英文名 | 适合的数据 | 典型 SPL |
|---|---|---|---|
| 柱状图 / 条形图 | Column / Bar | 分类对比 | stats count by status |
| 折线图 / 面积图 | Line / Area | 随时间变化趋势 | timechart span=1h count |
| 饼图 | Pie | 占比构成 | stats count by method |
| 单值 | Single Value | 一个关键 KPI | stats sum(bytes) as total |
| 地图 | Map / Choropleth | 带经纬度的事件 | ... | geostats count |
| 热力图 / 热力日历 | Heatmap | 密度分布 | ... | stats count by hour,weekday |
| 散点图 | Scatter | 两变量相关性 | ... | table latency,count |
| 趋势单值(雷达) | Radial Gauge | 指标是否越界 | stats avg(cpu) as cpu |
记住一句口诀:不同的 SPL 命令,喂给不同的图表。
timechart 产出"第一列是 _time、其余是数值序列"的表 —— 天生适合折线图、面积图、柱状图。stats count by 字段 产出"一个分类列 + 一个数值列" —— 适合柱状图、饼图。chart 比 stats 更偏可视化,chart count by status, hour 能直接画出"状态 × 小时"的堆叠柱。geostats / iplocation 配合地图面板,把 IP 变成地图上的热点。再展开说说什么时候该用哪种图,这是可视化里最容易被忽视、却最影响沟通效率的学问:
iplocation 命令)。记住一条铁律:图是为"让人快速做决定"服务的,不是用来炫技的。如果一个图需要对方盯着看 10 秒才能懂,那它就不合格,老老实实换成表格或换个图。
举例:想看"每小时各种 HTTP 状态码的堆叠柱状图",可以这样写:
index=web sourcetype=access_combined
| timechart span=1h count by status
在面板的 Visualization 里选 "Column"(柱状图),Splunk 会自动把每个 status 当成一根柱子的一摞颜色。如果想换成折线,只需把图表类型切到 "Line",底层数据一行都不用改 —— 这就是 Splunk "数据"与"展示"分离的好处。
点鼠标能搞定 80% 的需求,但当你要批量生成、版本管理、或做复杂交互时,就得懂一点 Simple XML。它就是一段普通的 XML,用标签描述仪表盘结构。下面是一段最小可用的仪表盘,包含一个面板、一条搜索、一张柱状图:
<dashboard>
<label>网站流量总览</label>
<description>从零到高手的第一个仪表盘</description>
<row>
<panel>
<title>每小时请求量</title>
<chart>
<search>
<query>index=web sourcetype=access_combined | timechart span=1h count</query>
<earliest>-24h@h</earliest>
<latest>now</latest>
</search>
<option name="charting.chart">column</option>
<option name="charting.legend.placement">bottom</option>
</chart>
</panel>
</row>
</dashboard>
结构拆解:
<dashboard> 根标签,声明这是一个仪表盘(表单用 <form>)。<label> / <description> 是仪表盘的标题和说明。<row> 是一行,里面放若干 <panel>。<panel> 里面可以是 <chart>(图)、<table>(表)、<single>(单值)、<map>(地图)等。<search> 里的 <query> 是 SPL,<earliest>/<latest> 是时间范围(-24h@h 表示往前 24 小时并对齐到整点)。<option> 是图表参数,charting.chart 决定图表类型(column / line / pie / area ...)。把这段保存成 my_dashboard.xml 放到 App 的 local/data/ui/views/ 目录下,重启或刷新即可在界面看到。它就是所有高级仪表盘的"积木"。
关于这段 XML,再强调几个新手必踩的细节:
<earliest> / <latest> 里的时间写法 -24h@h:-24h 是"往前 24 小时",@h 是"对齐到整点"。加 @h 的好处是每次刷新窗口边界一致,不会因为你在 9:37 打开就查 9:37 往前,而是规整到 9:00,利于缓存和同比对照。< 和 > 是特殊字符,如果 SPL 里出现比较符号(如 status>=500),必须写成实体 >,否则 XML 解析会报错。这是手写 XML 最常见的报错来源。<row> 里放几个 <panel> 由宽度决定,经典仪表盘里一行通常放 1~3 个面板,太多会挤成蚂蚁大小。<panel>...</panel> 粘进同一行或新行即可,结构极具"积木感"。当你在 Web 编辑器里改完仪表盘点保存,Splunk 实际上就是把你拖拽的结果反序列化回这段 XML存盘。所以"看 XML"和"用编辑器"本质是一回事,只是两种人机界面。
不想碰 XML?Splunk Web 自带的可视化编辑器(Dashboard Editor)就是为你准备的。流程是:
编辑器底层其实就是在帮你生成 Simple XML(或 Dashboard Studio 的 JSON),所以"拖拽"和"手写 XML"最终是殊途同归。建议新手先用拖拽建立直觉,再回头读 10.7 的 XML,会有"啊原来如此"的感觉。
好看的仪表盘只是第一步,能交互才是高手。两个关键点:
① 配色主题。Simple XML 里用 charting.* 系列 option 控制,例如:
<option name="charting.seriesColors">[0x1f77b4,0xff7f0e,0x2ca02c]</option>
<option name="charting.backgroundColor">0xffffff</option>
<option name="charting.axisTitleX.text">时间</option>
如果是 Dashboard Studio,可以在右侧主题面板里直接选配色方案,或用 JSON 里的 "visualizations" 字段精细控制。
② Drill-down(钻取)。这是 Splunk 仪表盘最迷人的特性:点击图上的一个点,带着被点击的值跳到另一个搜索 / 仪表盘 / 外部链接。例如你点了"5xx 错误"那根柱子,希望自动钻取到"该时间段 5xx 的明细"。经典 XML 里这样开:
<drilldown>
<link target="_blank">search?q=index=web status=500 earliest=$earliest$ latest=$latest$</link>
</drilldown>
更常见的是用 <set> 把点击的字段值存进 token(比如 $click.value$),再用这个 token 去联动另一个面板,实现"点 A 图,B 图跟着变"的联动大屏。
Token(令牌)是 Simple XML 交互的灵魂。你可以把它理解成一个"页面级的变量",值可以被点击、被下拉框、被时间选择器改变,然后绑定到面板的搜索里。比如一个大屏左边选"机房 A",右边所有面板就只显示机房 A 的数据。经典写法如下:
<input type="dropdown" token="dc">
<label>选择机房</label>
<choice value="dc_a">机房A</choice>
<choice value="dc_b">机房B</choice>
<default>dc_a</default>
</input>
<panel>
<chart>
<search>
<query>index=web datacenter=$dc$ | timechart count</query>
</search>
</chart>
</panel>
这里 $dc$ 就是 token,下拉框一变,搜索里的 $dc$ 跟着变,面板自动重跑。这个机制也是 Dashboard Studio 里 "Input" + "Data Source" 联动的思路源头。
常见坑提醒:钻取默认是"整行点击都触发",你可以用 <drilldown field="status"> 限定只有点 status 这一列才钻取;另外,token 名字别和 Splunk 内置变量(如 $earliest$、$latest$)撞车,否则时间范围会乱掉。
从 Splunk 8.0 起推出了 Dashboard Studio,它和经典 Simple XML 仪表盘的主要区别:
| 维度 | 经典仪表盘(Simple XML) | Dashboard Studio(JSON) |
|---|---|---|
| 描述语言 | Simple XML | JSON(基于 React) |
| 布局 | 行/列网格 | 绝对定位 + 网格,更自由 |
| 主题与配色 | 手动 option | 内置主题,一键切换深浅色 |
| 可视化种类 | 传统图表 | 新增平行坐标、箱线图等 |
| 数据源 | 直接绑搜索 | 独立 Data Source 层,可复用 |
简单说:经典仪表盘靠 XML、生态成熟、资料多;Dashboard Studio 更现代、更好看、布局更灵活,是新项目的推荐方向。两者都能在 Splunk Web 里通过"新建仪表盘"时选择框架。
搜索是你主动去看,告警(Alert)是 Splunk 替你定时/实时去看,发现异常就"拍你一下"。它的完整链路是:
一条保存的搜索 → 按调度运行 → 判断触发条件 → 执行动作(发邮件 / 调 Webhook / 跑脚本 / 写回索引)。
所以告警不是凭空产生的,它一定依附于一条保存的搜索(Saved Search)。这也是为什么我们前面反复强调"先把搜索固化成 Report"。
| 类型 | 运行方式 | 适用场景 |
|---|---|---|
| 实时告警(Real-time) | 持续监听数据流,事件一进来就判断 | 秒级发现、欺诈、入侵检测 |
| 计划告警(Scheduled) | 按 cron 周期跑搜索再判断 | 5xx 错误率、每日报表、容量预警 |
实时告警又分两种触发模式:per-result(每条结果) —— 每来一条满足条件的事件就触发一次;滚动窗口(Rolling Window) —— 在最近 N 分钟内聚合,达到阈值才触发。实时告警对系统资源消耗大,能用电计划告警就别用实时。
无论哪种告警,"什么时候算异常"由触发条件决定。Splunk 提供三类:
status>=500),当满足条件的结果占比超过阈值时触发。例如"5xx 占比超过 1%"。这里有个容易混淆的点要澄清:"结果数量"和"百分比"到底差在哪?假设你搜出 10000 条访问日志,其中 50 条是 5xx。"结果数量 > 100" 触发说的是"总量超过 100 条"——可 10000 条早就触发了,毫无意义;而"百分比 > 1%" 说的是"5xx 占总量超过 1%"——50/10000=0.5%,没触发,符合预期。所以监控比例类指标务必用百分比触发,监控绝对量指标(比如"失败任务数 > 0")才用结果数量。
在 savedsearches.conf 里,这些条件的对应配置大致是:
# 结果数量型:返回行数 > 100 触发
alert.type = number of results
alert.comparator = greater than
alert.threshold = 100
# 百分比型:status>=500 的占比 > 1% 触发
alert.type = percentage
alert.comparator = greater than
alert.threshold = 1
alert.condition = status>=500
触发之后干什么?Splunk 的动作(Action)非常丰富:
最常用。可以带结果内联表格、附 CSV,或把整个仪表盘导成 PDF 发过去。对应的 conf:
action.email = 1
action.email.to = oncall@example.com, boss@example.com
action.email.subject = [告警] $name$ 触发
action.email.message.alert = 告警名称:$name$\n触发时间:$trigger_time$
Webhook 是 Splunk 向一个 URL 发起 HTTP POST,把告警 JSON 推过去。国内团队常用来推到钉钉机器人或企业微信。步骤如下:
企业微信自定义机器人要求的 payload 形如:
{
"msgtype": "markdown",
"markdown": {
"content": "**Splunk 告警**\n> 名称:{{name}}\n> 触发时间:{{trigger_time}}\n> 结果数:{{results.length}}"
}
}
注意:Splunk 原生 Webhook 动作默认发的是 Splunk 自己的 JSON 结构,要对接钉钉/企微,要么用它们的"Splunk 集成"App,要么写一段自定义脚本/中间件做字段转换。社区里也有现成的 alert_actions 插件直接支持钉钉。
当内置动作不够用时,可以让 Splunk 调用一台机器上的脚本(比如调用内部故障自愈接口、打电话、写 CMDB)。脚本放在 $SPLUNK_HOME/bin/scripts/ 下,conf 里这样配:
action.script = 1
action.script.filename = page_oncall.sh
action.script.param = $name$ $results.count$
脚本会收到告警结果作为标准输入(JSON 或 CSV,取决于配置),你可以在脚本里随便二次加工。
有时你不只是要"通知",还想"把这次的异常结果存下来做长期分析",这时用汇总索引(Summary Index)动作,把结果写进另一个索引:
action.summary_index = 1
action.summary_index._name = summary
之后你就能 index=summary 去统计历史告警、做趋势图。这比每次都重算原始日志快得多,是"以空间换时间"的经典手法。
告警抑制(Suppression)与告警疲劳。如果一条告警每分钟都触发,邮箱会被刷爆,最后没人看了——这叫"告警疲劳",比没告警还危险。Splunk 提供两种机制缓解:一是 alert.digest_mode = 1(摘要模式),把一段时间内的多次触发合并成一封邮件;二是抑制窗口(Suppression),触发后一段时间内不再重复触发,例如"同一 host 5 分钟内只报一次"。配置示例:
alert.suppress = 1
alert.suppress.period = 300
alert.suppress.fields = host
意思是:对同一个 host,触发后 300 秒内不再发。生产环境强烈建议开启,否则半夜被同一故障轰炸的一定是你。
动作选择决策表:发给人看用邮件/Webhook;要自动执行某操作(重启、自愈)用脚本;要长期留存做分析用写回索引;要给第三方系统(ITSM、ChatOps)用 Webhook。多数严肃场景会同时挂多个动作,比如"邮件通知 oncall + Webhook 推钉钉群 + 写回 summary 索引留痕"。
计划告警和定时报表都靠 cron 表达式。Splunk 用的是标准的 5 段 cron:
分 时 日 月 星期
* * * * *
│ │ │ │ └─ 星期几 (0-7, 0和7都是周日)
│ │ │ └─── 月份 (1-12)
│ │ └───── 日 (1-31)
│ └─────── 小时 (0-23)
└───────── 分钟 (0-59)
* 表示"每",*/5 表示"每 5 个单位",9-18 表示范围,1,15 表示列举,1-5 在星期位表示工作日。
| 需求 | cron 表达式 | 说明 |
|---|---|---|
| 每 5 分钟 | */5 * * * * | 分钟位每 5 跳一次 |
| 每天上午 9 点 | 0 9 * * * | 9:00:00 整触发 |
| 工作日每 15 分钟(9-18 点) | */15 9-18 * * 1-5 | 工作时间高频巡检 |
| 每周日零点 | 0 0 * * 0 | 周报基准时间 |
| 每月 1 号凌晨 | 0 0 1 * * | 月度报表 |
在 savedsearches.conf 里挂载调度:
cron_schedule = */5 * * * *
dispatch.earliest_time = -15m@m
dispatch.latest_time = now
enableSched = 1
这里 -15m@m 表示"从 15 分钟前、对齐到分钟"开始查,配合"每 5 分钟跑一次",能避免漏算和重复算。
cron 的两个实战心法:
-1m),那 1~5 分钟之间的数据可能在不同次运行里被重复统计或漏掉。一般让窗口略大于间隔(如间隔 5 分钟、窗口取 10~15 分钟),再配合 dedup 或 streamstats 去重,最稳妥。server.conf 的 serverTimeZone 影响),不是你浏览器的时区。跨国团队常在这里踩坑:"说好每天 9 点发日报,怎么是北京时间 9 点其实是 UTC 9 点?"部署时务必统一时区配置。另外,Splunk 还有个便利设定叫 schedule_window:当搜索头在某一时刻任务太挤时,允许告警在 ±窗口内延迟执行,避免调度雪崩。高负载环境建议打开。
很多老板不看 Splunk,只看每天早上的邮件 PDF。做法:
PDF Report 可以指定把哪个 dashboard 渲染成 PDF),填收件人。对应的 conf 关键项:
action.email = 1
action.email.to = report-group@example.com
action.email.sendpdf = 1
action.email.pdf.view = /servicesNS/admin/myapp/search/dashboards__web_overview
action.email.subject = 每日网站流量报表
小提示:PDF 渲染依赖 Splunk 自带的 headless 浏览器(pdfgen),在容器/无图形环境里偶尔需要装字体,遇到"PDF 空白"先查 pdfgen* 日志。
整合前面所有知识点,做一个生产级告警:每 5 分钟检查一次,最近 15 分钟内 5xx 占比超过 1% 就发邮件 + 推钉钉。
步骤 1:写搜索并保存为 Report
index=web sourcetype=access_combined earliest=-15m@m latest=now
| stats count as total, count(eval(status>=500)) as err5xx
| eval ratio = round(err5xx*100.0/total, 2)
步骤 2:设置调度
cron_schedule = */5 * * * *
dispatch.earliest_time = -15m@m
dispatch.latest_time = now
步骤 3:设置触发条件(百分比型)
# 子条件:只统计 5xx
alert.type = percentage
alert.comparator = greater than
alert.threshold = 1
alert.condition = status>=500
或者用更直观的"结果数量"思路:让搜索直接算出 ratio,再用 where ratio>1 过滤,触发条件选 "Number of results > 0"。两种写法都行,后者更易懂:
index=web sourcetype=access_combined earliest=-15m@m latest=now
| stats count as total, count(eval(status>=500)) as err5xx
| eval ratio = round(err5xx*100.0/total, 2)
| where ratio > 1
alert.type = number of results
alert.comparator = greater than
alert.threshold = 0
步骤 4:设置动作
action.email = 1
action.email.to = oncall@example.com
action.email.subject = [P1] 网站 5xx 错误率超阈值:$result.ratio$%
action.webhook = 1
action.webhook.param.url = https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN
完整串起来,一条 savedsearches.conf 的段落大概是:
[Web 5xx 错误率告警]
search = index=web sourcetype=access_combined earliest=-15m@m latest=now | stats count as total, count(eval(status>=500)) as err5xx | eval ratio=round(err5xx*100.0/total,2) | where ratio>1
cron_schedule = */5 * * * *
dispatch.earliest_time = -15m@m
dispatch.latest_time = now
enableSched = 1
alert.type = number of results
alert.comparator = greater than
alert.threshold = 0
alert.digest_mode = 1
action.email = 1
action.email.to = oncall@example.com
action.email.subject = [P1] 5xx 错误率超阈值
action.webhook = 1
action.webhook.param.url = https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN
保存后,Splunk 就会每 5 分钟默默帮你盯着错误率,一旦破 1%,邮件和钉钉同时响 —— 你把命交给它,它替你值夜班。
这个实战里藏着的三个工程要点,建议你刻进肌肉记忆:
eval 把"判断"前置到搜索里。我们把 where ratio>1 写进搜索,触发条件就退化成最简单的"有结果就报警"。这样做的好处是:触发逻辑一眼可见、易调试;如果改用"百分比触发",你还得额外维护一段子条件搜索,更容易出错。$result.ratio$ token。Splunk 允许在动作里引用结果的字段值,这样 oncall 在手机邮件列表里扫一眼标题就知道多严重,不用点开。类似的还有 $trigger_time$、$results.count$。把这套"观察基线 → 设阈值 → 挂动作 → 验证 → 调阈值"的循环跑顺,你就具备了生产级可观测性建设的核心能力。
Splunk 的强大不在本体,而在生态。理解两个词是入门生态的第一课:
| 维度 | App(应用) | Add-on(插件) |
|---|---|---|
| 定位 | 面向人的"成品",带界面、仪表盘、报表 | 面向数据的"零件",负责采集/解析/富化 |
| 有没有前端 | 通常有完整 UI(菜单、仪表盘) | 通常无界面,只在后台工作 |
| 典型内容 | 仪表盘、 saved search、导航、脚本 | inputs.conf、props.conf、transforms.conf、lookups |
| 举例 | Splunk Enterprise Security、ITSI、MLTK | Splunk Add-on for AWS、各类 TA |
| 关系 | App 常依赖一个或多个 Add-on 提供数据 | Add-on 可独立存在,被多个 App 复用 |
这句话记忆:App 是"看得见的成品",Add-on 是"看不见的管道"。比如你想分析 AWS 数据,先装 Splunk Add-on for AWS(管道,负责把 CloudTrail/S3 日志拉进来并解析好),再装 Splunk App for AWS(成品,提供现成的仪表盘)。
为什么要把"管道"和"成品"分开?这是 Splunk 架构里非常聪明的一招:解析逻辑(Add-on)应该和数据源绑定,而不是和某个展示 App 绑定。因为同一份 AWS 日志,安全团队要用(ES)、运维团队也要用(ITSI)、业务团队也要用,如果解析规则写在某个 App 里,其他 App 就用不了或要重复写。把解析抽到独立的 TA/Add-on 里,谁都能复用,字段名也全公司统一。这条经验直接决定了你后面做数据规范时的目录规划。
还有一个相关概念叫 SA(Supporting Add-on):它不放采集配置,只放"共享的知识对象"(比如 CIM 数据模型、通用提取规则),专门给多个 App 当底座。CIM(Common Information Model)是 Splunk 定义的一套"标准字段命名法",ES、ITSI 都基于 CIM 工作。所以你会看到生态里三类东西:TA 负责采集解析、SA 负责共享知识、App 负责展示——三者配合,才是完整的 Splunk 数据工程。
Splunkbase(splunkbase.splunk.com)是官方的 App / Add-on 应用市场。任何人都能发布,Splunk 官方和社区都往上面传。需要注意:Splunk 并不支持 Splunkbase 上的所有应用,下载前要看清楚支持类型(Splunk 支持 / 社区支持 / 不支持)。选择 App 时重点看三件事:
进入 Apps → Manage Apps → Browse more apps,搜索名字,点 Install。如果服务器不能联网,可以先在能上网的机器从 Splunkbase 下载 .tgz 包,再到 Manage Apps → Install app from file 上传安装。安装后通常需要重启 Splunk 才能生效。
# 从本地文件安装
/opt/splunk/bin/splunk install app /tmp/splunk_app_aws.tgz -auth admin:password
# 安装后重启让 App 生效
/opt/splunk/bin/splunk restart
# 查看已安装的 App 列表
/opt/splunk/bin/splunk display app
如果是集群环境(Search Head Cluster),要用 splunk apply shcluster-bundle 把包含新 App 的 bundle 推到各搜索头,而不是在单节点上直接装。
安装时的两个关键注意事项:
splunk 运行用户(通常是 splunk 或 root 据部署而定),否则 Splunk 进程读不到会报权限错误。装完可用 chown -R splunk:splunk /opt/splunk/etc/apps/my_first_app 修正。local/ 目录、官方默认放 default/ 目录,Splunk 优先读 local,这样升级 App 不会冲掉你的个性化设置。这条 local 优先于 default 的规则,贯穿 Splunk 所有配置。| 名称 | 类型 | 解决什么问题 |
|---|---|---|
| Splunk App for Stream | App + Add-on | 无需改应用,就能从网络流量里抓协议级数据(数据库、HTTP、DNS 等),做深度可见性 |
| Splunk Enterprise Security (ES) | App(付费) | 安全运营中心(SOC)底座:态势感知、关联分析、合规报表 |
| Splunk IT Service Intelligence (ITSI) | App(付费) | IT 运维:服务树、KPI、告警降噪、根源分析 |
| Splunk Machine Learning Toolkit (MLTK) | App(免费) | 内置大量 ML 算法,做异常检测、预测、聚类,无需自己写模型 |
| Splunk Add-on for *(AWS / Windows / Linux / Kubernetes…) | Add-on | 针对某一数据源的采集与解析"管道" |
| 各类 Technology Add-on (TA) | Add-on | 提供某类设备或日志的字段提取规范(见 12.5) |
新手建议先把 MLTK 和对应数据源的 TA 装上,体会"装一个 App,立刻多出一堆仪表盘和字段"的爽感。
对这几个重量级 App 再多说两句,帮你判断要不要上:
AnomalyDetect)、预测(Predict)、聚类等算法做成 SPL 命令和向导式助手,无需写 Python。例如 ... | anomalycount 就能标出离群点。适合做"没有固定阈值的智能告警"。这是 Add-on 里最值得懂的一块。TA 的核心价值是"统一解析规范":它打包了一组配置文件,告诉 Splunk 怎么把某种原始日志变成结构化字段。
props.conf:定义来源的源类型(sourcetype),指定用哪种时间戳、行分隔、字符集,并指向提取规则。transforms.conf:定义字段提取(FIELD_EXTRACTION)、改写、路由、掩码等具体变换逻辑。lookups/:附带的查询表,用于把原始值富化成可读字段(如把数字状态码映射成含义)。举个最小例子,一个 TA 的 props.conf 片段:
[access_combined]
SHOULD_LINEMERGE = false
TIME_PREFIX = \[
TRANSFORMS-extract = extract_fields
对应的 transforms.conf:
[extract_fields]
REGEX = (\S+) (\S+) (\S+) \[(.*?)\] "(\S+) (\S+) (\S+)" (\d{3}) (\d+)
FORMAT = clientip::$1 user::$2 time::$4 method::$5 uri::$6 status::$7 bytes::$9
装了这个 TA 之后,所有 sourcetype=access_combined 的日志都会被自动抽出 clientip、status 等字段,你在搜索里直接写 status=500 就能用,不必每次手写 rex。这就是"数据规范"的力量 —— 全公司用同一套 TA,字段名就不会张三叫 ip、李四叫 client_ip。
props.conf 与 transforms.conf 的协作关系要讲透:props.conf 是"在哪个 sourcetype 上、用什么方式处理"的入口,它用 TRANSFORMS-xxx = 名字 指向 transforms.conf 里定义的真正变换;transforms.conf 才是干活的。一个 sourcetype 可以挂多个 TRANSFORMS,按顺序执行。常见的变换类型有四种:
脱敏例子(把手机号掩码):
# props.conf
[access_combined]
SEDCMD-maskphone = s/1[3-9]\d{9}/***/g
富化例子(把状态码数字映射成文字):
# transforms.conf
[status_lookup]
filename = status_code.csv
# props.conf
[access_combined]
LOOKUP-status = status_lookup status OUTPUT status_text
其中 status_code.csv 形如 status,status_text\n500,Internal Server Error。这样搜索里就能直接用 status_text 字段。TA 把这些"脏活累活"打包好,使用者零成本拿到干净数据——这正是 Add-on 存在的全部意义。
当你发现团队有一套通用解析 / 仪表盘想复用,就可以把它打包成自己的 App。一个最小 App 的目录结构如下:
my_first_app/
├── app.conf # App 的身份证:名字、版本、作者、可见性
├── default/
│ ├── data/
│ │ └── ui/
│ │ └── views/ # 放 Simple XML 仪表盘(.xml)
│ │ └── overview.xml
│ ├── savedsearches.conf # 报表 / 告警定义
│ ├── props.conf # 解析规则(如果这是个 TA)
│ ├── transforms.conf # 提取规则
│ └── navigation.conf # 左侧菜单(App 才有)
├── lookups/ # 富化用的查询表(.csv)
│ └── status_code.csv
├── bin/ # 自定义脚本 / 模块化告警
│ └── my_alert.py
└── metadata/
└── default.meta # 权限:谁可见、谁可用
最少只需 app.conf 就能让 Splunk 识别这是一个 App。一个能跑的 app.conf 示例:
[install]
is_configured = 1
[ui]
is_visible = 1
label = 我的第一个 App
[launcher]
author = 张三
description = 团队通用的 Web 日志分析 App
[package]
id = my_first_app
check_for_updates = 0
把这整个目录打成 my_first_app.tar.gz,用 12.3 的 splunk install app 装上,重启,左侧应用列表里就多了一个属于你自己的 App。从"用别人的 App"到"造自己的 App",就是你从 Splunk 使用者迈向 Splunk 工程师的分水岭。
自建 App 的进阶建议:
TA-myweb 只放 props/transforms/lookups,myweb_app 只放仪表盘并依赖 TA。这样解析可以给安全、运维多个 App 复用,符合 12.1 讲的"管道与成品分离"。default.meta 决定权限:写成 [ ] 段 + access = read : [ * ], write : [ admin ] 表示所有人可读、仅管理员可改。权限配错会导致同事打不开你的仪表盘。app.conf 的 [launcher] 或 [package] 里记录版本,配合 Git 管理 local/ 下的配置,你的 App 就具备了可迭代、可回滚的工程能力。走到这一步,你已经不是在"用 Splunk",而是在"用 Splunk 搭建属于团队的数据产品"了。这,就是从零到高手的全部含义。
至此,《Splunk 从零到高手》的"可视化 → 告警 → 生态"三座大山已经翻过:你会用图表把数据讲清楚,会用告警让系统替你盯梢,也懂得借助 Splunkbase 海量 App 和自建 App 把能力沉淀成团队资产。剩下的,就是在真实业务里反复练手了 —— Splunk 的尽头不是记住多少命令,而是养成"任何日志问题,都能拆成 采集 → 解析 → 搜索 → 可视化 → 告警"的习惯。
前面十二章我们把 Splunk 的原理、部署、SPL 语法、数据接入、仪表盘、告警都讲透了。但"纸上得来终觉浅"——这一章我们直接下场做三个真实业务场景,让你看到一条从原始日志到业务价值的完整链路。这三个案例覆盖了安全分析、IT 运维、业务分析三大主流方向,几乎能套用到你公司 80% 的需求里。读这一章的正确姿势:不要"看懂就行",而要在自己 Splunk 里把每条 SPL 跑一遍、改一改、再加一两个条件——真正的手感,是敲出来的。
每个案例都遵循同一套方法论:明确问题 → 确定数据源与字段 → 写 SPL 逐层下钻 → 沉淀为报表/告警/仪表盘。建议你打开 Splunk 搜索栏,把这些 SPL 一条条贴进去跑(用自己的索引名替换示例索引),感受数据如何被一层层"榨出"价值。
场景:某公司 Web 服务器每天产生数 GB 的 access log,安全团队想知道——谁在暴力破解我们的登录接口?哪些 IP 在做目录扫描?哪些请求慢得像蜗牛拖着服务器?下面我们一条 SPL 链解决这三个问题。
假设数据已接入,索引为 web_access,sourcetype 为 nginx:plus:access(也可用于 Apache / ELB / CDN 日志)。典型字段有:clientip、user、status、uri_path、request_time(秒)、method、bytes。
暴力破解的典型特征:同一个 clientip 对登录接口发起大量请求,且绝大多数登录失败(status=401/403)。我们先用 SPL 把"登录失败"的事件筛出来,按 IP 和账号聚合,找出失败次数异常高的组合。
index=web_access sourcetype=nginx:plus:access uri_path="/login" status=401
| stats count AS fail_count by clientip, user
| where fail_count > 20
| sort - fail_count
| eval risk = case(fail_count>200, "严重", fail_count>100, "高危", 1=1, "关注")
| table clientip, user, fail_count, risk
链路解读:
index= ... uri_path=... status=401:最左过滤,先用索引、路径、状态码三把"快刀"把数据量砍到最小——这是性能的第一要义(详见第14章)。stats count by clientip, user:把逐条事件聚合成 (IP, 账号) 维度的失败计数。比 transaction 快得多。where fail_count > 20:阈值过滤,去掉噪声。eval risk=case(...):给结果分级,方便后续告警分级处理。升级版:同时看"失败→成功"的翻盘账号(破解成功最危险)。用一个子搜索找出失败多但最近登录成功的账号:
index=web_access sourcetype=nginx:plus:access uri_path="/login"
[ search index=web_access sourcetype=nginx:plus:access uri_path="/login" status=200
| stats count by user | fields user ]
| stats
count(eval(status=401)) AS fails,
count(eval(status=200)) AS successes
by clientip, user
| where fails > 20 AND successes > 0
| sort - fails
| table clientip, user, fails, successes
这条 SPL 用子搜索(方括号)先取出"曾经登录成功过的账号"集合,再在主搜索里看这些账号里谁失败次数还特别高——正是"已经被撞库但仍在试探"的可疑账号,优先级最高。
扫描器的典型特征:单个 IP 在短时间窗口内请求大量不同的 uri_path(尤其是不存在的路径,返回 404)。我们用 dc()(去重计数)统计每个 IP 访问的不同路径数。
index=web_access sourcetype=nginx:plus:access status=404
| bin _time span=5m
| stats dc(uri_path) AS distinct_paths, count AS total_404 by clientip, _time
| where distinct_paths > 50
| sort - distinct_paths
| table _time, clientip, distinct_paths, total_404
要点:
bin _time span=5m:把时间分桶,看"5 分钟内"的扫描爆发,避免把一整天累计分散的 404 误判。dc(uri_path):统计不同路径数。注意 dc() 是相对昂贵的命令(见第14章"性能坑"),但用它做安全扫描识别非常合适,因为我们要的就是"路径多样性"。进一步,用 iplocation 把 IP 转成地理信息,配合威胁情报看板(这里简化为输出国家):
index=web_access sourcetype=nginx:plus:access status=404
| stats dc(uri_path) AS distinct_paths by clientip
| where distinct_paths > 50
| iplocation clientip
| table clientip, Country, distinct_paths
| sort - distinct_paths
慢请求用 request_time 字段(秒)。我们看 P95/P99 延迟、以及超过 3 秒的慢请求 Top N。
index=web_access sourcetype=nginx:plus:access
| bin _time span=1h
| stats
count AS reqs,
avg(request_time) AS avg_rt,
perc95(request_time) AS p95_rt,
perc99(request_time) AS p99_rt,
max(request_time) AS max_rt
by _time, uri_path
| where p95_rt > 1.0
| sort - p95_rt
| table _time, uri_path, reqs, avg_rt, p95_rt, p99_rt, max_rt
再下钻到具体的慢请求明细,定位是哪个客户端、哪个时间点:
index=web_access sourcetype=nginx:plus:access request_time>3
| sort - request_time
| head 50
| table _time, clientip, method, uri_path, request_time, status, bytes
把三件事串成一张安全仪表盘:分别把上面三组 SPL 存为报表(Report),再用"仪表盘编辑器"拖成三块面板(暴力破解 Top 榜、扫描器 IP、慢请求 P95 趋势),安全值班人员一眼就能看全局。
index=web_access sourcetype=nginx:plus:access uri_path="/login" status=401
| bin _time span=5m
| stats count AS fails by clientip, user
| where fails > 30
把这条存为告警,触发条件"结果数 > 0",每 5 分钟跑一次,阈值超过即通知安全群。这就是把"分析"变成"防守"。
场景:运维团队要把上百台服务器的 CPU、内存、磁盘使用率接进 Splunk,做到:① 实时看到指标;② 磁盘超过 85% 自动告警;③ 把海量错误日志聚类,避免被重复日志淹没。
Splunk 处理指标(metrics)有两条路:
df -h、top 输出当普通日志采集,再用 SPL 提取字段。适合小规模、临时。metrics.log 或 Collectd/Telegraf 通过 metrics sourcetype 写入,字段用 metric_name + _value 结构。适合大规模监控,查询用 mstats 命令,性能极高。以日志型为例,假设已用脚本采集磁盘使用率,事件形如:
host=web01 partition=/dev/sda1 mount=/var used_pct=87.3
实时看磁盘趋势:
index=os_metrics sourcetype=linux:disk
| bin _time span=5m
| stats avg(used_pct) AS disk_pct by _time, host, mount
| timechart span=5m avg(disk_pct) AS disk_pct by host
index=os_metrics sourcetype=linux:disk used_pct>85
| stats latest(used_pct) AS cur_pct by host, mount
| where cur_pct > 85
| eval severity = case(cur_pct>95, "紧急", cur_pct>90, "警告", 1=1, "注意")
| table host, mount, cur_pct, severity
存为告警,触发条件"结果数 > 0",通知运维值班。要点:用 latest() 取每个分区最新值,避免历史瞬时尖峰重复告警。
如果用原生 metrics,查询改为 mstats(速度更快,直接读指标摘要):
| mstats avg(_value) AS disk_pct WHERE index=os_metrics_metrics
AND metric_name=disk.used.pct BY host, mount
| where disk_pct > 85
当一台服务器疯狂报错,几万条 stack trace 其实只有 3、4 种根因。与其一条条看,不如用 cluster 命令把"长得像"的日志归成一类,看每类的数量和代表样本。
index=app_logs sourcetype=java:error level=ERROR
| cluster t=0.7 showcount=true label=cluster_label
| stats count AS cnt by cluster_label, _raw
| sort - cnt
| head 20
解读:
cluster t=0.7:相似度阈值,越大要求越像才归为一类。0.6~0.8 是常用区间。showcount=true:显示每类数量。进阶:把聚类结果配合 rare 命令找"新出现的错误模式"(异常检测):
index=app_logs sourcetype=java:error level=ERROR
| cluster t=0.7 label=c
| stats count by c, _raw
| rare limit=10 c
场景:电商平台要算转化率、看 Top 商品、看订单的地域分布。Splunk 不只是运维安全工具,它天然是一个业务分析平台——只要把业务事件(下单、支付、退款)规范地打进 Splunk。
假设索引 shop_events,字段:event_type(view/product/add_cart/order/pay)、product_id、category、user_id、province、amount。
index=shop_events sourcetype=shop:event
| bin _time span=1d
| stats
dc(eval(if(event_type="view", user_id, null))) AS uv_view,
dc(eval(if(event_type="add_cart", user_id, null))) AS uv_cart,
dc(eval(if(event_type="order", user_id, null))) AS uv_order,
dc(eval(if(event_type="pay", user_id, null))) AS uv_pay
by _time
| eval
cart_rate = round(uv_cart/uv_view*100, 2),
order_rate = round(uv_order/uv_cart*100, 2),
pay_rate = round(uv_pay/uv_order*100, 2),
total_cvr = round(uv_pay/uv_view*100, 2)
| table _time, uv_view, uv_cart, uv_order, uv_pay, cart_rate, order_rate, pay_rate, total_cvr
要点:用 dc(eval(if(...)) 在一条 stats 里同时算各阶段的"去重用户数",再用 eval 算各级转化率。这比写 4 条独立搜索再 join 高效得多(见第14章性能章节)。
index=shop_events sourcetype=shop:event event_type=pay
| stats
count AS orders,
sum(amount) AS gmv,
dc(user_id) AS buyers
by product_id, category
| sort - gmv
| head 20
| eval gmv = round(gmv, 2)
| table product_id, category, orders, buyers, gmv
再画 GMV 地域分布热力图(用 SPL 输出省份聚合,仪表盘选" choropleth 地图"面板):
index=shop_events sourcetype=shop:event event_type=pay
| stats sum(amount) AS gmv by province
| sort - gmv
仪表盘思路:把"转化率漏斗"做成折线/柱状组合图,"Top 商品"做成横向柱状图,"地域 GMV"做成中国地图着色,"实时支付金额"做成单值可视化(用 timechart sum(amount))。运营大屏就此成型。
最后把"支付失败率突增"做成业务告警,监控大盘健康度:
index=shop_events sourcetype=shop:event event_type IN ("order", "pay")
| bin _time span=10m
| stats count(eval(event_type="order")) AS orders,
count(eval(event_type="pay")) AS pays by _time
| eval pay_fail_rate = 1 - pays/orders
| where pay_fail_rate > 0.1
写完一条复杂 SPL,新手常卡在"跑出来不对,却不知哪步出错"。高手有一套调试心法:① 把长搜索从后往前逐段注释,每加一个管道(pipe)就执行一次,看中间结果是否符合预期;② 善用 table / fields 在中间步骤打印关键字段,确认字段名拼写与取值;③ 用 eval debug=... 临时打标,区分不同分支的数据流。例如排查"转化率算出来是 0"时,先单独跑各阶段 dc 看是否某个 event_type 根本没进来。
分析跑通后,要养成"自动化沉淀"的习惯:把成功的 SPL 存为已保存搜索(Saved Search),设置定时调度(如每 5 分钟/每小时),并在"告警动作"里配置通知——可以发邮件、写进 Webhook、推到 Slack/钉钉、或调用脚本触发工单。这样你从"手动 analysts"升级为"自动防御系统架构师"。一个好习惯:所有生产级告警都加 throttle(抑制),避免同一问题在 5 分钟内反复轰炸值班手机。
运维监控里常需要"给裸 IP/主机名补上业务含义"。比如把告警里的 host 通过 lookup 关联一张资产表,自动标出"这是核心支付服务器还是测试机",让告警分级更智能:
index=os_metrics sourcetype=linux:disk used_pct>85
| lookup asset_table host OUTPUT app_name, env, owner
| eval severity = case(used_pct>95 AND env="prod", "紧急-生产",
used_pct>90, "警告",
1=1, "注意")
| table host, app_name, env, owner, used_pct, severity
资产表 asset_table.csv 放在 Splunk 的 lookup 目录,通过 lookup 定义 关联。这是"可观测性"走向"业务可观测"的关键一步——监控不再只看数字,而是知道"这个数字背后是哪条业务线在受影响"。
运营大屏常需要"最近 15 分钟的实时转化"。用 earliest=-15m + timechart 即可,关键是让仪表盘面板的刷新间隔(refresh)设为 1 分钟,并配合 bin 把时间切成细粒度:
index=shop_events sourcetype=shop:event earliest=-15m
| bin _time span=1m
| stats dc(eval(if(event_type="view",user_id,null))) AS uv,
dc(eval(if(event_type="pay",user_id,null))) AS pay
by _time
| eval cvr = round(pay/uv*100,2)
| timechart span=1m avg(cvr) AS realtime_cvr
真实世界里,三种分析从不是孤立的。一个经典联动场景:某台 Web 服务器突然出现大量 500 错误(运维信号),同时发现来自同一批 IP 的暴力破解(安全信号),而该服务器承载的"支付下单"转化率同步跳水(业务信号)。当三域数据都在 Splunk 里,你可以用一条跨索引搜索把它们关联起来,形成一个"故障影响面"全景。
思路:先用一个子搜索找出"最近 1 小时被暴力破解的高危 IP 集合",再到 Web 访问索引和订单索引里看这些 IP 关联的服务器与转化表现:
index=web_access sourcetype=nginx:plus:access uri_path="/login" status=401
[ search index=web_access sourcetype=nginx:plus:access uri_path="/login" status=401
| stats count AS fails by clientip | where fails>30 | fields clientip ]
| stats dc(host) AS attacked_hosts, count AS login_failures by clientip
| join clientip
[ search index=app_logs sourcetype=java:error level=ERROR
| stats count AS app_errors by clientip ]
| table clientip, attacked_hosts, login_failures, app_errors
这条搜索把"攻击来源"和"它打挂了多少台机器、引发了多少应用错误"并排呈现。再叠加业务侧:
index=shop_events sourcetype=shop:event event_type=pay earliest=-1h
| stats sum(amount) AS gmv by host
| join host
[ search index=os_metrics sourcetype=linux:disk used_pct>85
| stats latest(used_pct) AS disk by host ]
| table host, gmv, disk
虽然前面反复强调"少用 join",但在跨索引、且两侧都已用 stats 压缩成小结果集的前提下,join 是合理且可控的——这正是第14章说的"避免裸 join 大表,但允许 join 已聚合的小结果"。把上面这些面板拼到一个"故障作战室(War Room)"仪表盘里,值班经理一眼就能判断:这是普通故障,还是一次有业务后果的安全事件。
更进一步,可以基于这套联动逻辑写关联告警:当"某 IP 在 5 分钟内既触发暴力破解告警、又导致目标服务器 500 错误率超过阈值"时,自动升级为高危安全事件并通知安全运营中心(SOC)。这已经是从"工具使用"迈向"安全运营体系设计"了。
index/sourcetype/时间/关键字段 永远写在最前面。stats,又快又准。Splunk 用得爽不爽,90% 取决于是否懂性能。很多新手抱怨"搜索怎么要 30 秒",一查 SPL:*error* 前缀通配 + 跨 90 天 + 一个 join 三张表。这一章我们把性能要诀、存储规划、字段提取权衡、知识对象规范一条条讲清,并附一张"优化前后对比"表。
在动手优化前,先建立一个核心认知:Splunk 的搜索是"流式管道"——数据从索引层一边读、一边经过你写的每个命令(pipe)被加工,最终吐出结果。性能的好坏,取决于"最早的那几步能不能把数据量砍下来"以及"每个命令本身的代价"。Splunk 内部把命令按代价大致分为流式(streaming,逐事件本地计算,最快)、落地(transforming,需要汇总,稍慢)、集中(centralized,需把数据拉到搜索头,最慢)。理解这一点,你就明白为什么"把 stats 尽量往前放、把 sort/dedup 尽量往后放"能提速——因为前者在数据进搜索头前就压缩了体量。
index、sourcetype、时间范围、具体字段值 是最强力的"索引裁剪"手段。Splunk 的 bloom filter 和 TSIDX 能据此跳过大量无关桶(bucket)。
index=web_access sourcetype=nginx:plus:access status=500 earliest=-1hindex=* 然后 search "error" 再慢慢过滤——全索引扫描,灾难。*foo 这种"前面带星号"的搜索无法利用倒排索引,Splunk 只能逐事件扫描,极慢且耗资源。非要模糊匹配,优先用后缀或包含:
uri_path="*admin*"uri_path="*/admin" 或用 uri_path IN ("/admin", "/admin/*")database 配置 allowStarSuffix 或字段级索引,但尽量避免。join 是 Splunk 里最容易被滥用的命令。它把两张结果集在内存里做关联,大数据量下既慢又吃内存,还可能丢数据(默认只取右表 5 万行)。绝大多数"关联"需求都能用 stats 的"分组聚合"替代:
// 反例:用 join 关联订单和支付
index=shop_events event_type=order
| join user_id [ search index=shop_events event_type=pay | stats sum(amount) AS paid by user_id ]
| table user_id, paid
// 正解:一次 stats 搞定
index=shop_events event_type IN ("order","pay")
| stats
count(eval(event_type="order")) AS orders,
sum(eval(if(event_type="pay", amount, 0))) AS paid
by user_id
需要跨索引关联时,优先考虑 stats 先把两边都算好再 append/union,或用 tstats(见下)。
tstats 命令不读原始事件,直接读 TSIDX(时间序列索引)和加速数据模型的摘要文件,速度通常是普通 stats 的几十到上百倍。前提是字段得是索引字段(indexed field)或属于已加速的数据模型。
// 对“已加速的 Web 数据模型”做极速统计
| tstats count FROM datamodel=Web WHERE Web.status=500 BY Web.src, _time span=1h
| timechart sum(count) AS errors by Web.src
// 对索引字段(如 host、sourcetype、_time)直接 tstats
| tstats count WHERE index=os_metrics BY host, _time span=5m
下表把同一业务诉求的"裸写 SPL"与"优化后 SPL"做对比,左边是新手常见写法,右边是高手写法,附性能差异(数据为经验量级,随数据规模浮动)。
| 优化维度 | ❌ 优化前(慢) | ✅ 优化后(快) | 性能收益 |
|---|---|---|---|
| 过滤位置 | index=* | search status=500 |
index=web_access status=500 earliest=-1h |
扫描量↓ 90%+,提速 5~20× |
| 通配符 | uri_path="*login*" |
uri_path IN ("/login","/login/*") |
避免全扫描,提速 3~50× |
| 表关联 | ... | join user_id [subsearch] |
... | stats ... by user_id |
省内存、无截断,提速 2~10× |
| 大数据聚合 | index=web ... | stats count by src |
| tstats count FROM datamodel=Web BY Web.src |
读 TSIDX,提速 10~100× |
| DISTINCT 计数 | | stats dc(ip) by uri(全量) |
加时间窗 bin + 阈值过滤或近似 |
减少冗余计算,提速 2~5× |
| 汇总复用 | 每次大盘刷新都算原始统计 | 定时塞进 summary 索引,面板读汇总 |
面板加载从秒级→毫秒级 |
下面用一张 SVG 直观展示"优化前(全索引扫描)"与"优化后(索引裁剪 + tstats)"的资源消耗差异:
存储规划是"看不见但最致命"的环节。很多团队初期随便给 Splunk 挂块盘,等业务量上来才发现:要么磁盘被冷数据塞满、索引器拒绝写入(MinFreeSpace 触发),要么全用 SSD 导致成本爆炸。好的规划要回答三个问题:数据要留多久?(保留期)、热数据要撑多快?(介质选型)、超期数据要不要归档?(冻结与召回)。下面先讲清底层的 bucket 生命周期,再给配置与 license 规划。
Splunk 的索引(index)在底层由一个个 bucket(桶,约 750MB~10GB 的数据块)组成,按数据"年龄与活跃度"在存储层间流动:
在 indexes.conf 里配置分层路径与保留期:
[web_access]
homePath = $SPLUNK_DB/web_access/db # 热+温
coldPath = $SPLUNK_DB/web_access/colddb # 冷
thawedPath = $SPLUNK_DB/web_access/thaweddb # 解冻恢复
frozenTimePeriodInSec = 2592000 # 30天后冻结
maxTotalDataSizeMB = 500000 # 索引总容量上限
# 若用 SmartStore 把冷数据放 S3:
# remotePath = volume:s3/web_access
# storageType = remote
Splunk 9 起大力推广 SmartStore:热/温数据本地,冷数据自动落到对象存储(S3/MinIO),大幅降低本地磁盘成本,且支持按需"召回"冻结数据。
Splunk 的 license 是按每日索引数据量(GB/天)计费的。超量不"断服",但会触发 license violation 警告(5 次违规窗口内仍可搜,超出则搜索受限)。规划要点:
license_usage.log 监控实际消耗:index=_internal sourcetype=splunkd source=*license_usage.log type=Usage
| stats sum(b) AS bytes by idx
| eval GB = round(bytes/1024/1024/1024, 2)
| sort - GB
| table idx, GB
这是新手最容易踩的坑,也是面试高频题。很多人在 props.conf 里辛苦写了提取规则,却困惑"为什么改了之后历史数据没变"——答案就藏在"提取时机"里。Splunk 有两种提取字段的时机,二者在查询速度、灵活性、索引开销上各有权衡,选错会导致"要么搜索慢如牛,要么改规则要对新数据重来一遍"。
| 维度 | 搜索时提取(Search-time,默认推荐) | 解析时提取(Index-time) |
|---|---|---|
| 何时提取 | 搜索执行时才按 props.conf/transforms.conf 规则抽取 |
数据写入索引时就写进 TSIDX 索引 |
| 查询速度 | 较慢(需运行时解析) | 极快(字段已索引,可直接 tstats) |
| 灵活性 | 高,随时改提取规则且对历史数据生效 | 低,规则改动只对新数据生效 |
| 索引开销 | 小 | 大(写索引更慢、占更多磁盘) |
| 适用场景 | 绝大多数字段(90% 场景) | 高频查询的关键字段(如 user、src_ip、status) |
经验法则:默认全部用搜索时提取。只有当某个字段被海量查询且极度频繁(如安全场景的 user、src)时,才考虑写进 fields.conf 做索引时提取。例如:
// fields.conf 声明为索引字段
[user]
INDEXED = true
// props.conf 配合 transforms 在解析时抽取
[my_sourcetype]
TRANSFORMS-user = extract_user
// transforms.conf
[extract_user]
REGEX = user=(\S+)
FORMAT = user::$1
当团队有 50 个人、上千个报表/宏/lookup 时,乱命名会让系统变成"屎山"。推荐规范:
[业务域]_[对象]_[动作],如 web_bruteforce_alert、os_disk_threshold_alert。`get_last_7d` 之类可复用片段,命名动词化。[用途]_[版本].csv,如 threat_ip_feed_2026.csv。app:web、app:os,配合 CIM 的 tag=web。clientip/src_ip/c_ip 统一别名成 CIM 标准 src,便于跨源关联。数据模型是把底层杂乱日志"抽象成有层次的对象"(如 Web 模型含 Web 对象、Web 对象下挂 sessions/errors)。它本质是"给一类日志建一个结构化的、可复用的查询视图"——业务人员不用懂 SPL,也能在仪表盘里直接拖"Web 模型 → 按响应码统计",背后的复杂提取与计算被封装在模型里。开启加速后,Splunk 后台把模型涉及的字段预计算成 TSIDX 摘要文件,相当于"提前把答案算好存着"。此后用 tstats 查询该模型,速度飞起。
什么时候该建数据模型?经验法则:当某个数据集会被反复、高频地用于报表/仪表盘/ES 关联搜索,且数据量巨大时,就值得为它建加速模型。反之,一次性探索性搜索没必要建模型,反而增加存储和维护负担。建模时遵循"窄而准"原则:只加速真正被查询的字段和对象,加速范围(如 90 天)也不要贪大,否则摘要文件会膨胀、后台重建也会拖慢索引器。
tsidx 文件存于 $SPLUNK_DB/<index>/datamodel_summary/。如果说"数据模型加速"是 Splunk 帮你自动建摘要,那么汇总索引就是你手动把"算好的结果"存起来复用。典型场景:有一个"全国门店每日销售汇总"的大盘,每次打开都要对 30 天、数十亿条原始订单重算——这显然不可接受。正确做法:建一个定时搜索,每小时算一次增量汇总,写进一个专门的 summary 索引;大盘直接读这个汇总索引,毫秒级出图。
做法一:用 collect 命令把结果写进汇总索引:
index=shop_events sourcetype=shop:event event_type=pay earliest=-1h@h latest=-0h@h
| stats sum(amount) AS gmv, count AS orders BY province, _time
| collect index=summary_shop_gmv addtime=true
做法二:在已保存搜索里设置"汇总索引"为输出目标(UI:Edit Saved Search → 勾选 Summary Indexing,选目标索引)。之后大盘 SPL 极简:
index=summary_shop_gmv
| timechart span=1h sum(gmv) AS gmv by province
注意:collect 默认会把 _time 带上,且建议在写入时打上 summary 标记字段方便区分;汇总索引要单独设保留期(通常比原始索引短)。这是"以空间换时间"的经典工程权衡。
传统的"温/冷本地磁盘"在超大数据量下成本高企。SmartStore 把冷数据(及部分温数据)放到对象存储(S3、GCS、MinIO、Azure Blob),本地只留热数据缓存。好处:索引器扩缩容无需迁移 TB 级数据——新节点启动后按需从 S3 拉 bucket;成本大幅下降。配置核心在 indexes.conf 的 remotePath 与 server.conf 的 [storage-volume:s3]:
[indexes]
// server.conf 定义对象存储卷
[storage-volume:s3]
storageType = remote
path = s3://my-bucket/splunk
// indexes.conf 让某索引用 SmartStore
[web_access]
remotePath = volume:s3/web_access
homePath = $SPLUNK_DB/web_access/db
maxCacheSize = 500GB
SmartStore 是"存储与计算分离"的云原生思路,也是 Splunk 在容器化/云上弹性伸缩的基石。
dc() 需要去重,比 count() 贵数倍。大数据集上尽量加时间窗、或用近似算法(如配合 estdc 估算)。stats + streamstats 替代。maxout),超了会被截断导致结果不准;尽量先 stats 压缩子搜索结果。search ... 不设 earliest 会默认读全部历史;仪表盘务必设 earliest=$time_token$。| regex ... 逐事件执行;热点正则应在 props/transforms 里预定义或做索引字段。summary 索引而非每次重算原始日志(见 14.3)。当你的数据量从"一台服务器够用"长到"几十 TB/天、要 7×24 高可用、跨机房容灾"时,单实例 Splunk 就不够了。这一章讲清 Splunk 的分布式架构:索引器集群如何保证数据不丢、搜索头集群如何扛并发、多站点如何做容灾,以及部署服务器、CIM、Splunk Cloud 等高级话题。
在讲原理前,先回答一个根本问题:为什么单台索引器不够? 因为单台索引器一旦宕机,它上面的数据就查不到、写入也中断;磁盘坏了,数据直接丢失。索引器集群用"多副本 + 主节点协调"把单点故障变成"可容忍的常态"。它的设计哲学是:宁可多花几倍存储,也绝不允许数据丢失或查询中断——这正是生产环境对日志平台的底线要求。
索引器集群由三类角色组成:
server.conf 里的 [clustering] 配置。replication factor 把 bucket 副本同步给其他 peer。配置示例(在主节点 server.conf):
[clustering]
mode = master
replication_factor = 3
search_factor = 2
pass4SymmKey = <密钥>
cluster_label = prod_cluster
对等节点配置:
[clustering]
mode = peer
master_uri = https://10.0.0.10:8089
pass4SymmKey = <密钥>
replication_port = 9887
在大规模集群里,转发器(forwarder)要往哪些 peer 写数据?索引器发现让 forwarder 主动向主节点询问"当前有哪些 peer 在线",主节点返回 peer 列表,forwarder 据此自动负载均衡地发送数据。好处是:peer 扩缩容、故障切换时,forwarder 无需改配置自动感知。
forwarder 的 outputs.conf 关键配置:
[indexer_discovery://cluster1]
master_uri = https://10.0.0.10:8089
pass4SymmKey = <密钥>
[tcpout:group1]
indexerDiscovery = cluster1
useACK = true
这样 forwarder 不再硬编码 peer 的 IP,而是"问主节点要地址",集群拓扑变化对它完全透明。
搜索头是用户直接交互的"前台"。单搜索头有两个硬伤:并发扛不住、一旦宕机所有人失业。搜索头集群用"多成员 + 队长选举 + 部署器统一下发"解决这两个问题,让用户连任意一个成员都能得到一致体验。
当单搜索头扛不住并发,或要做高可用时,用搜索头集群。SHC 角色:
建议至少 3 个成员以保证 quorum(法定多数)。配置搜索头为集群成员:
[shclustering]
mode = captain # 或 member
id = <唯一ID>
master_uri = https://10.0.0.10:8089 # 指向索引器集群主节点
pass4SymmKey = <密钥>
admitter_ip = <本机IP>
跨机房/跨地域容灾用多站点集群。把 peer 分到 site1、site2,用 site 级复制因子和site 级搜索因子保证"每个站点都有完整副本":
[clustering]
mode = master
multisite = true
site_replication_factor = origin:2, site1:1, site2:1, total:4
site_search_factor = origin:1, site1:1, site2:1, total:2
available_sites = site1,site2
含义:原始写入站点存 2 份,site1/site2 各 1 份,共 4 份;任一整个站点宕掉,另两个站点仍能搜索到完整数据。这是金融、政务等"不允许丢数据"场景的标配。
下面这张图把"转发器 → 索引器集群(主+对等+复制)→ 搜索头集群(队长+成员+部署器)→ 用户"的完整链路画出来:
当你有几百台 forwarder 要统一下发配置(inputs.conf、outputs.conf、TA 插件),一台台改是不现实的。部署服务器集中管理:
deployment.conf / serverclass.conf 里定义服务器类(server class),把 forwarder 分组,绑定对应的配置应用(app)。// serverclass.conf 示例
[serverClass:linux_web]
whitelist.0 = web-*
[serverClass:linux_web:app:props_web]
[serverClass:linux_web:app:outputs_cluster]
restartSplunkd = true
CIM(Common Information Model,通用信息模型)是 Splunk 的"普通话"——它定义了一套标准字段名和标签,把来自防火墙、IDS、Web 服务器、终端等不同厂商、不同格式的日志,映射到统一的语义层。例如无论是 Cisco 还是 Palo Alto 的防火墙日志,CIM 都把它归一化为 src、dest、action 等标准字段。没有 CIM,安全分析师就得为每一种设备写一套独立规则;有了 CIM,一套基于标准字段的关联搜索就能"通吃"所有符合 CIM 的数据源——这正是 Splunk ES 能快速接入上百种安全产品、而不必为每个产品重写检测逻辑的根本原因。
CIM 不是自动生效的魔法:它需要各个技术插件(TA,Technology Add-on)来完成"原始字段 → CIM 标准字段"的映射。TA 通常做三件事:正确解析日志(props/transforms)、用 FIELDALIAS 把厂商字段别名成 CIM 标准字段、用 tags.conf 给事件打上 tag=web、tag=authentication 之类的 CIM 标签。当你发现 ES 的某个预置关联搜索"没产出",第一反应就该查:我的数据有没有被正确归一化到对应 CIM 模型?用 CIM Validation 工具(Splunkbase 上的 Common Information Model Add-on 自带)可以一键检测数据模型的字段覆盖率。
在 Splunk Enterprise Security(ES)里,CIM 是命根子:
Splunk Add-on for Cisco ASA)的职责之一就是把原始日志归一化到 CIM(通过字段别名、tags.conf 打 tag=network 等)。示例:给 Web 数据打上 CIM 标签,让 ES 能识别:
// tags.conf
[eventtype=web_access_event]
web = enabled
// props.conf 里把 clientip 别名成 CIM 标准字段 src
[nginx:plus:access]
FIELDALIAS-clientip = clientip AS src
EVAL-action = case(status>=400, "failure", 1=1, "success")
KPI:在 ITSI(IT Service Intelligence)里,KPI 是从数据算出的关键指标(如"订单服务的平均响应时间"),支撑服务健康度评分与玻璃表(Glass Table)。
"上云还是自建?"是选型时绕不开的问题。两者底层引擎完全一致,差异在谁运维、谁担责、控制到哪一层。简单说:Splunk Cloud 把"装系统、打补丁、管备份、扩磁盘"这些脏活交给厂商,你只管往里灌数据和做分析;自托管则把这一切责任都交给你,换来的是对底层配置的完全掌控(比如某些合规要求禁止数据出域、或需要自定义底层文件系统)。下面是详细对比,帮助你根据团队体量、合规要求和工程能力做选择。
| 维度 | Splunk Cloud(托管) | Splunk Enterprise(自托管) |
|---|---|---|
| 运维责任 | Splunk/Cisco 管底层硬件、升级、备份 | 你管一切(OS、磁盘、集群、升级) |
| 上线速度 | 快,开箱即用 | 慢,需自备基础设施 |
| 控制粒度 | 受限(部分底层配置不可改) | 完全可控 |
| 成本模型 | 订阅制,按 ingestion 计费 | License + 自有硬件/云资源 |
| 适用 | 中小团队、不想养运维、合规可接受上云 | 大型/强合规/数据不出域场景 |
集群出问题时,靠"猜"是最低效的。生产环境的故障往往发生在深夜、伴随告警风暴,此时你最需要的不是灵感,而是一套从配置到运行态、从索引器到搜索头的标准化排查清单。下面的命令和思路,建议在新手期就逐个在测试集群里跑熟——等到真出事时,肌肉记忆会救你一命。记住排障总原则:先看状态(谁不健康),再看配置(btool 实际生效值),最后看日志(_internal),三层递进,不要一上来就改配置。
集群出问题时,靠"猜"是最低效的。Splunk 提供一组排障利器:
$SPLUNK_HOME/bin/splunk cmd btool indexes list --debug 看索引配置来源;$SPLUNK_HOME/bin/splunk cmd btool props list <sourcetype> --debug 看字段提取规则。splunk show cluster-status 或访问 UI 的"Indexer Clustering"面板,看各 peer 的 RF/SF 是否满足、是否有 bucket 处于未复制状态。splunk show shcluster-status 看队长是谁、各成员是否健康、最近一次同步时间。splunk search "index=_internal sourcetype=splunkd component=IndexerService" 找索引写入报错。index=_internal Metrics group=queue 里 blocked 持续升高,说明管道(parsing/merging/indexing queue)被压满,通常源于突增数据量或磁盘 IO 瓶颈,需扩容或限速。联邦搜索让一个 Splunk 部署的搜索头去查询另一个 Splunk 部署的数据,而无需把数据集中搬运。场景:集团总部要搜各子公司的 Splunk,或云上 Splunk 要查本地 Splunk。配置:在"Settings → Federated Search → Federated Providers"里添加对端 Splunk 的 URI、认证信息;之后 SPL 里用 federated:<provider> 前缀即可跨域查询。注意跨域查询的网络延迟与权限映射,适合"偶尔查、不常驻"的整合需求。
走到这一章,你已经从"什么是 Splunk"走到了"集群、性能、业务实战"。最后这章给你一张从零到高手的 6 阶段路线图,把官方文档、免费环境、书籍社区、认证体系一次性打包,让你知道"下一步往哪走"。
很多人学 Splunk 半途而废,不是因为难,而是因为"没有节奏"——今天看两页文档,明天装个环境又放下,半年后还是只会 index=*。这一章的价值,就是给你一个可执行的、有反馈闭环的进阶节奏:每一步都有明确产出物(你能跑的命令、能接的数据、能做的仪表盘),让你每次合上电脑都有"我又前进了一步"的实感。坚持走完这 6 阶段,你就会发现:曾经看起来高不可攀的"架构师"岗位,其实只是把每一步都做扎实后的自然结果。
/var/log,也能跑通"数据流动"全链路。stats、eval、timechart、transaction、lookup、rex。SPL 是 Splunk 的"编程语言",内功越深越值钱。学习 Splunk,资源的质量比数量重要。下面这份清单经过大量学习者验证,按"先用哪个、再用哪个"的顺序排好,避免你在浩如烟海的资料里迷路。核心原则:文档查权威、实战练手感、社区补坑,三者缺一不可。
书和社区解决的是"文档不会告诉你的最佳实践"——那些踩过坑的人才懂的捷径与雷区。下面按"入门→进阶→交流"分层推荐。
splunk 标签。认证不是目的,而是能力的外在证明。在求职市场,一张 Splunk Certified Admin 或 Architect 往往是简历通过初筛的"硬门槛";在职场内部,认证帮你系统性补齐知识盲区——因为备考会逼你把平时"只会用、没深究"的配置、命令、架构都啃一遍。需要提醒的是:Splunk 于 2024 年成为 Cisco 一员后,认证体系持续演进,部分旧认证被标注为 Legacy(遗留),同时新增了 O11y、Cybersecurity Defense 等面向新战略的方向。下面这张表以"当前名称 + 经典考试代码"双标注,方便你对照新旧资料。
截至 2026 年,Splunk(现 Cisco 旗下)的认证体系已演进为"Core / Cloud / Enterprise / 安全"多条线。下表列出当前主流认证及其对应的经典考试代码(旧代码如 SPLK-1001 仍在培训和题库中广泛引用,便于你对照资料):
| 认证名称(当前) | 经典考试代码 | 面向角色 | 核心考点 |
|---|---|---|---|
| Splunk Core Certified User | SPLK-1001 | 使用者/业务分析 | 基础搜索、字段、报表、仪表盘、告警 |
| Splunk Core Certified Power User | SPLK-1002 | 高级用户 | SPL 命令、知识对象、字段别名、宏、数据模型 |
| Splunk Core Certified Advanced Power User | (新,无旧码) | 专家用户 | 复杂搜索/报表、高级知识对象、仪表盘最佳实践 |
| Splunk Enterprise Certified Admin | SPLK-1003 | 管理员 | license、inputs、索引器/搜索头、配置、监控、数据接入 |
| Splunk Cloud Certified Admin | (Cloud 专属) | 云管理员 | Splunk Cloud 日常管理、inputs/forwarders、问题隔离 |
| Splunk Enterprise Certified Architect | SPLK-2001 | 架构师 | 分布式部署规划、容量规划、集群与排障 |
| Splunk Core Certified Consultant | SPLK-2002 | 实施顾问 | 大型部署、多层级架构、集群与扩展 |
| Splunk Enterprise Security Certified Admin | SPLK-2003(Legacy) | 安全管理员 | ES 环境管理、TA、风险分析、威胁情报 |
| Splunk IT Service Intelligence (ITSI) Certified Admin | (Legacy) | ITSI 管理员 | ITSI 安装配置、KPI、玻璃表、深度分析 |
| Splunk Certified Cybersecurity Defense Analyst / Engineer / Architect | (新安全线) | 安全运营 | 威胁检测、关联规则调优、SOAR 自动化 |
| Splunk O11y Cloud Certified Metrics User | (可观测线) | 可观测用户 | Metrics 监控、OpenTelemetry、实时告警 |
备考建议(血泪经验):
eval、stats、lookup、macros、field aliases、data models。建议在自己环境里把每个知识对象都亲手建一遍。inputs.conf / props.conf / transforms.conf / indexes.conf / server.conf 五件套,动手搭一遍"1 主 + 2 对等 + 1 搜索头"的最小集群。index=* "关键字" 兜底确认数据是否进了预期索引。最常见原因是 sourcetype 解析错了或时间戳提取偏移。license_usage.log 查询提前预警,并考虑优化高噪声数据源。maxspan),否则一律用 stats。transaction 在大事件量下内存消耗惊人。props.conf / transforms.conf(或通过 UI 的"字段提取"知识对象);改完对历史数据立刻生效,这是搜索时提取的最大优势。splunk show cluster-status 观察进度,期间搜索不受影响。| 资源类型 | 名称 / 地址 | 用途 |
|---|---|---|
| 官方文档 | help.splunk.com / docs.splunk.com | 命令、配置权威参考 |
| 场景实战库 | Splunk Lantern (splunk.com/lantern) | 按问题找 SPL 范例 |
| 免费实验 | Splunkbase 上的 Enterprise 试用镜像 | 浏览器里直接练手 |
| 免费课 | Splunk Free Training (Fundamentals 1) | 零基础入门视频 |
| 书籍 | 《Splunk Operational Intelligence Cookbook》 | 案例驱动进阶 |
| 社区 | Splunk Answers / r/Splunk / Stack Overflow | 提问、查坑 |
| 插件市场 | Splunkbase (splunkbase.splunk.com) | TA、ES 内容包、仪表盘 |
回头看——第1章你连 index=* 都不敢敲,现在已经能:用 SPL 揪出暴力破解者、用集群保证数据不丢、用 tstats 把查询提速百倍、用 CIM 把异构日志说"同一种语言"。这趟从零到高手的旅程,你走完了最难、也最有价值的一段。
Splunk 的真正威力,不在于某个命令多花哨,而在于它把"机器的噪音"翻译成"人能决策的信息"。安全分析师用它抓住黑客,运维用它守住 SLA,业务用它发现增长机会——而你,已经握住了这把钥匙。
最后送你三句话,作为这本《Splunk 从零到高手》的收尾:
去接你的第一份真实数据吧,高手之路,由此开始。祝你在 Splunk 的世界里,所搜即所得。🚀
下面列出编写本教程时参考的权威资料与社区文章,建议结合官方文档深入。Splunk 的命令行、配置名与 SPL 命令请以 官方文档 docs.splunk.com 为准,因为不同大版本(如 8.x 与 9.x/10.x)在默认配置与部分命令上会有差异。
以下来自微信公众号的连载与发布,可作为中文语境下的补充学习材料(经搜狗微信检索整理):
本教程为教学用途的整合编写,部分命令与界面随版本演变,请以你实际环境的官方文档为准。祝你早日从“Splunk 小白”进阶为“搜索高手” 🟡