Splunk
概述
GreptimeDB 实现了 Splunk HTTP Event Collector (HEC) 协议的一个子集。已经在用 HEC 的采集器——比如 Vector 和 OpenTelemetry Collector——只需要改一下地址和 token 就能写入 GreptimeDB。
提供两个写入端点:
/services/collector/event接收结构化的 JSON 事件。/services/collector/raw接收纯文本,原样存储。
Splunk 的 index 对应 GreptimeDB 的表。host、source、sourcetype 以及 fields 下的各个 key 会成为 Tag 列,GreptimeDB 会把它们加进表的主键。
不支持 indexer acknowledgment(/services/collector/ack)。channel 参数会被接收但忽略。
Fluent Bit 自带的 splunk output 用不了这个端点。它把请求路径写死成 /services/collector/event,也没有提供配置路径的选项,所以在没有反向代理的情况下访问不到 GreptimeDB 的 /v1/splunk。要从 Fluent Bit 写入,请改用它的 HTTP output。
HTTP API
基础路径是 /v1/splunk,采集路径由客户端自己拼接。把采集器指向 http://${db_host}:${db_http_port}/v1/splunk 即可,例如 http://localhost:4000/v1/splunk。
| 端点 | 方法 | 用途 |
|---|---|---|
/v1/splunk/services/collector/event | POST | 写入 JSON 事件。别名:/services/collector、/services/collector/event/1.0 |
/v1/splunk/services/collector/raw | POST | 写入纯文本。别名:/services/collector/raw/1.0 |
/v1/splunk/services/collector/health | GET | 健康检查。别名:/services/collector/health/1.0 |
请求体支持 gzip 压缩(Content-Encoding: gzip)。
鉴权
只有在服务端配置了 user provider 时才会校验鉴权。没有配置时请求直接放行。
HEC 客户端发送的是 Authorization: Splunk <token>,GreptimeDB 把这个 token 按 username:password 解析:
Authorization: Splunk greptime_user:greptime_pwd
Authorization: Basic <base64> 同样接受。
大多数 Splunk 客户端默认用一个不含冒号的 opaque token,这种 token 会被 GreptimeDB 拒绝并返回 HEC code 4。请把客户端的 token 配置成 <username>:<password> 的形式。
健康检查端点是公开的,任何情况下都不需要 token。
写入 JSON 事件
POST /v1/splunk/services/collector/event
请求体是一个或多个 HEC 事件对象。它们可以直接拼接、用换行或空格这类 JSON 空白字符分隔,也可以放在一个顶层 JSON 数组里。其他分隔符(比如逗号、分号)会导致解析失败,请求返回 HEC code 6。
字段映射
| HEC 字段 | 对应到 |
|---|---|
time | greptime_timestamp 时间索引 |
index | 表名 |
host、source、sourcetype | Tag 列 |
fields 下的各个 key | Tag 列,每个 key 一列 |
event 及其余 key | 数据列 |
time 是 epoch 秒,可以带小数。大于等于 1e12 的值按毫秒解析。time 缺失或为 null 时使用服务端的写入时间。
time 存在但解析不了不会回退,而是直接报错,请求返回 HEC code 6。raw 端点的 ?time= 同理。
表名路由
表名按以下顺序确定:
- 事件里的
index,会被规整成合法表名。[A-Za-z0-9_:\-.@#]之外的字符替换成_,首字符不合法时在前面补一个下划线。例如my index/2026会变成my_index_2026。 tablequery 参数。- 默认表
splunk_logs。
Query 参数
| 参数 | 说明 |
|---|---|
db | 目标数据库,默认 public。 |
table | 事件里没有 index 时使用的兜底表名。 |
pipeline_name | 使用的 Pipeline,默认是内置的 greptime_identity。 |
version | Pipeline 版本,默认使用最新版本。 |
这个端点只认上面这几个参数。其他日志写入参数如 msg_field、custom_time_index、ignore_errors 在这里不生效。
示例
curl -X POST "http://localhost:4000/v1/splunk/services/collector/event" \
-H "Authorization: Splunk greptime_user:greptime_pwd" \
-H "Content-Type: application/json" \
-d '{"time":1785312000,"host":"web-01","source":"/var/log/nginx/access.log","sourcetype":"nginx:access","index":"splunk_events","fields":{"env":"prod","region":"us-west"},"event":{"status":200,"path":"/api/v1/health","latency_ms":12}}
{"time":1785312001,"host":"web-02","source":"/var/log/nginx/access.log","sourcetype":"nginx:access","index":"splunk_events","fields":{"env":"prod","region":"eu-central"},"event":{"status":503,"path":"/api/v1/orders","latency_ms":841}}'
写入成功返回:
{"code":0,"text":"Success"}
第一次写入时会自动建表 splunk_events:
DESC TABLE splunk_events;
+--------------------+---------------------+------+------+---------+---------------+
| Column | Type | Key | Null | Default | Semantic Type |
+--------------------+---------------------+------+------+---------+---------------+
| greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP |
| env | String | PRI | YES | | TAG |
| event.latency_ms | Int64 | | YES | | FIELD |
| event.path | String | | YES | | FIELD |
| event.status | Int64 | | YES | | FIELD |
| host | String | PRI | YES | | TAG |
| region | String | PRI | YES | | TAG |
| source | String | PRI | YES | | TAG |
| sourcetype | String | PRI | YES | | TAG |
+--------------------+---------------------+------+------+---------+---------------+
默认 Pipeline 会把嵌套的 event 对象打平,所以数据列的名字是 event.status、event.path、event.latency_ms。
SELECT * FROM splunk_events ORDER BY greptime_timestamp;
+---------------------+------+------------------+----------------+--------------+--------+------------+---------------------------+--------------+
| greptime_timestamp | env | event.latency_ms | event.path | event.status | host | region | source | sourcetype |
+---------------------+------+------------------+----------------+--------------+--------+------------+---------------------------+--------------+
| 2026-07-29 08:00:00 | prod | 12 | /api/v1/health | 200 | web-01 | us-west | /var/log/nginx/access.log | nginx:access |
| 2026-07-29 08:00:01 | prod | 841 | /api/v1/orders | 503 | web-02 | eu-central | /var/log/nginx/access.log | nginx:access |
+---------------------+------+------------------+----------------+--------------+--------+------------+---------------------------+--------------+
后续写入里 fields 新出现的 key 会自动加成 Tag 列并加入主键。
写入纯文本
POST /v1/splunk/services/collector/raw
请求体原样存进 message 列,GreptimeDB 不解析内容。如果需要结构化,用自定义 Pipeline 读 message 字段。
默认情况下整个请求体是一个事件,这样堆栈这类多行内容能保持完整。要把请求体切成多个事件,用 linebreaker 传入分隔符(需要百分号编码),例如 ?linebreaker=%0A 按换行切分。只含空白的片段会被丢弃。
这个端点的元数据是请求级的:来自 query 参数,对请求里的所有事件生效。
Query 参数
| 参数 | 说明 |
|---|---|
host、source、sourcetype | Tag 列,对请求内所有事件生效。 |
index | 表名,规整规则和 /event 的 index 字段一致。 |
time | 对所有事件生效的 epoch 时间戳,默认使用写入时间。 |
table | 没有 index 时的兜底表名。 |
linebreaker | 用于切分请求体的分隔符字面量,需要百分号编码。不传时整个请求体是一个事件。 |
pipeline_name | 使用的 Pipeline,默认 greptime_identity。 |
version | Pipeline 版本。 |
db | 目标数据库,默认 public。 |
channel | 接收但忽略。也可以用 x-splunk-request-channel 请求头传。 |
示例
printf 'line one\nline two\nline three\n' | curl -X POST \
"http://localhost:4000/v1/splunk/services/collector/raw?index=splunk_raw&host=web-01&source=app.log&sourcetype=applog&linebreaker=%0A" \
-H "Authorization: Splunk greptime_user:greptime_pwd" \
--data-binary @-
这会写入三行。同样的请求去掉 linebreaker 则只写入一行,内容是完整的三行文本。
SHOW CREATE TABLE splunk_raw;
CREATE TABLE IF NOT EXISTS `splunk_raw` (
`greptime_timestamp` TIMESTAMP(9) NOT NULL,
`host` STRING NULL,
`message` STRING NULL,
`source` STRING NULL,
`sourcetype` STRING NULL,
TIME INDEX (`greptime_timestamp`),
PRIMARY KEY (`host`, `source`, `sourcetype`)
)
ENGINE=mito
WITH(
'comment' = 'Created on insertion',
append_mode = 'true'
)
使用 Pipeline
两个端点都会让每个事件走一遍 Pipeline。默认是内置的 greptime_identity,它把输入的每个 key 映射成同名的列。
可以用 pipeline_name query 参数或 x-greptime-pipeline-name 请求头覆盖,两者同时存在时 query 参数优先。用 version 可以指定 Pipeline 版本。
把 host、source、sourcetype 和 fields 的 key 提升为 Tag 列只发生在默认的 greptime_identity 路径上。自定义 Pipeline 自己决定输出 schema,需要索引哪些列要在 Pipeline 里自己声明。
响应码
响应体是 {"text": ..., "code": ...},和 HEC 一致,客户端根据 code 分支处理。
| Code | 含义 |
|---|---|
0 | 成功 |
2 | 缺少 token |
4 | token 非法 |
5 | 没有数据 |
6 | 数据格式错误,包括 time 或 version 解析失败 |
7 | index 错误,即最终解析出的表名不合法 |
8 | 服务端内部错误 |
12 | 缺少 event 字段 |
13 | event 字段为空 |
17 | 健康检查端点返回,表示 collector 正常 |
校验失败会让整个请求失败。一批事件里只要有一条缺 event、event 为空,或者 time 存在但解析不了,这一批就一条都不会写入——端点不会跳过坏事件继续写其余的。host、source 这类可选元数据缺失不影响写入。
Vector
Vector 的 splunk_hec_logs sink 不需要额外改造就能写入 GreptimeDB:
[sinks.greptime]
type = "splunk_hec_logs"
inputs = ["my_source"]
endpoint = "http://localhost:4000/v1/splunk"
default_token = "greptime_user:greptime_pwd"
index = "vector_logs"
compression = "gzip"
encoding.codec = "json"
endpoint:填/v1/splunk基础路径,不要带采集路径,Vector 会自己拼。default_token:必须是<username>:<password>,不能是 opaque token。index:目标表名。encoding.codec:这个 sink 的必填项。endpoint_target:默认event,走/services/collector/event。设成raw则走 raw 端点。
改用 raw 端点:
[sinks.greptime]
type = "splunk_hec_logs"
inputs = ["my_source"]
endpoint = "http://localhost:4000/v1/splunk"
default_token = "greptime_user:greptime_pwd"
index = "vector_raw_logs"
endpoint_target = "raw"
encoding.codec = "text"
batch.max_events = 1
Vector 批量发送时事件之间没有分隔符,所以在 raw 端点上一整批会被当成一个事件。像上面这样设 batch.max_events = 1,或者用默认的 event 模式即可。
服务端的 linebreaker 本来可以把这样一批切开,但这个 sink 送不出去:它没有配置任意 query 参数的选项,把参数拼到 endpoint 上也不行——Vector 是把 endpoint 和采集路径按字符串直接拼接的,http://localhost:4000/v1/splunk?linebreaker=%0A 会拼成 /v1/splunk?linebreaker=%0A/services/collector/raw?index=...,路由匹配不上。能自由设置 query 参数的 HEC 客户端可以正常使用 linebreaker。
OpenTelemetry Collector
splunk_hec exporter 的配置方式类似:
exporters:
splunk_hec:
endpoint: "http://localhost:4000/v1/splunk/services/collector"
token: "greptime_user:greptime_pwd"
index: "otel_logs"
tls:
insecure: true
service:
pipelines:
logs:
receivers: [otlp]
exporters: [splunk_hec]
和 Vector 不同,这个 exporter 的 endpoint 需要填完整的采集路径。