Osmanthe 网关:怎么测试、怎么接 OpenTelemetry
2026-10-10 · 第一版骨架 · Rust 网关 + VictoriaLogs v1.53.0
cargo test 单元测试、tools/smoke_test.sh 端到端检查、tests/regression/ 回归测试、telemetrygen / hec_replay.py 压测。任何改动上线前都要跑 bash tools/check_all.sh,前三层全部通过才算完成。
1. 现在能做什么
采集端 Osmanthe 网关 (Rust) 存储
Splunk UF / Fluent Bit / Vector ──► HEC /services/collector/* ┐
OTel SDK / OTel Collector ────────► OTLP/HTTP /v1/logs ├─► LogStore 接口 ─► VictoriaLogs
OTel SDK / OTel Collector ────────► OTLP/gRPC :4317 │ (租户 = AccountID/ProjectID)
浏览器 / 脚本 ─────────────────────► 搜索 /api/v1/search (LogsQL) ┘
- Key 只写日志:HEC 用
Authorization: Splunk <key>,OTLP 用Authorization: Bearer <key>。搜索要先登录(POST /api/v1/login),令牌放X-API-Key。冒烟测试和回归测试用超级管理员(密码见 deploy/docker-compose.yml 的 OSMANTHE_SUPERADMIN_PASSWORD)给每个租户重设一个 checks-租户名 管理员再登录(tools/test_logins.sh)。 - 租户隔离:租户只由 Key 决定,请求内容里的任何参数都换不了租户。已用两个租户互相查询验证过。
- 不丢数据的前提:VictoriaLogs 确认写入之后,网关才给客户端返回成功;VictoriaLogs 挂了就返回 503,OTLP 和 HEC 客户端会自动重试。
- 换引擎:存储只通过
LogStore这个 trait 调用,以后换 ClickHouse 只需要新增一个模块。 - 支持 gzip:请求体可以 gzip 压缩,大小上限按解压后计算,防压缩炸弹。
2. 现在就能看
打开 https://coding.lab.sigmax-group.com,用 demo 租户的账号登录(没有的话让超级管理员在「租户」页给 demo 设一个管理员),时间范围选“全部”,然后点搜索。里面已经有今天测试写进去的约 570 万条日志。
可以试试这些查询:
error:全文搜索sourcetype:replay "500":HEC 写进来的模拟 nginx 日志service.name:checkout severity:ERROR:OTLP 写进来的日志* | stats by (service.name) count():按服务统计条数
更多写法和搜索页用法见 搜索页用法与 LogsQL 速查(2026-10-11 新增)。
换成 other-key-change-me 再搜,只能看到另一个租户自己的几条测试数据,这就是租户隔离。
3. 四层测试
规则:每个功能、每次修改都必须通过全部回归测试。一条命令跑完格式检查、clippy、单元测试、冒烟测试和回归测试:
docker compose -p coding -f deploy/docker-compose.yml up -d --build gateway # 先部署新代码
bash tools/check_all.sh
最后一行出现 CHECK_ALL PASSED 才算通过。
第 1 层:单元测试和接口测试(不需要启动 VictoriaLogs)
cargo test
cargo clippy --all-targets -- -D warnings
一共 38 个测试,包括 HEC 解析和 Splunk 错误码、时间戳精度、OTLP 的 JSON 和 protobuf 转换、鉴权、gzip、内存存储下的租户隔离、SPL 翻译。CI 配置在 .github/workflows/ci.yml,推到 GitHub 后自动运行。
第 2 层:端到端冒烟测试(真实 VictoriaLogs)
docker compose -p coding -f deploy/docker-compose.yml up -d --build
docker compose -p coding -f deploy/docker-compose.yml --profile loadgen up -d otel-collector # 可选
bash tools/smoke_test.sh
脚本会跑 20 项检查:HEC 的 event、raw、gzip 三种写法,OTLP 的 HTTP JSON 和 gRPC(用官方 telemetrygen),经过真实 OTel Collector 转发的写入,搜索,坏查询,没带 Key 或 Key 错误,以及两个方向的租户隔离。每项都会核对存进去的条数是否一条不差。今天的结果:20/20 通过。
第 3 层:回归测试(真实 VictoriaLogs)
bash tests/regression/run.sh # 全部用例
bash tests/regression/run.sh '' 001 # 只跑名字里带 001 的用例
每修好一个 bug,就在 tests/regression/cases/ 下加一个用例文件,编号递增,文件开头写明日期、现象、原因和对应的单元测试。用例自己写入带唯一标记的数据(index=regtest),不依赖演示数据,所以随时可以重复跑。修复前先确认新用例会失败,修复后再确认通过。
| 编号 | 问题 | 检查什么 |
|---|---|---|
| 001 | 上面「匹配 X 条事件」和 stats count 对不上(2026-10-11) | 开始时间不在整点时,hits 总数、柱状图各柱之和、stats count 三者相等;覆盖 SPL 和 LogsQL、多种分桶、只有开始时间的情况 |
| 002 | 全角竖线 | 查询没反应(2026-10-11) | 全角 | ( ) = 写的 SPL 和半角写法结果一样 |
回归测试写入的数据很少(每次几条),都在 index=regtest 下,搜索时加 index!=regtest 可以排除。
第 4 层:压测
OpenTelemetry 写入:用官方工具 telemetrygen
docker run --rm --network coding_default \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
logs --otlp-endpoint gateway:4317 --otlp-insecure \
--otlp-header 'authorization="Bearer demo-key-change-me"' \
--duration 60s --rate 0 --workers 6 --batch-size 1000 --service load-test
改成走 HTTP:加上 --otlp-http,并把地址换成 gateway:8080。
真实日志:用 tools/hec_replay.py 流式回放,原始日志不落盘
# 客户给的压缩样本,边解压边发,100G 的 VPS 也能回放远超 100G 的日志
zstd -dc 客户样本.log.zst | python3 tools/hec_replay.py \
--token demo-key-change-me --sourcetype nginx --concurrency 8
# 没有真实数据时,用带高基数字段(IP、用户、trace_id)的模拟数据
python3 tools/hec_replay.py --generate 5000000 --token demo-key-change-me
这个脚本只用 Python 标准库,也能直接对着真的 Splunk HEC 跑,方便同一份数据横向对比(前提是 Splunk 许可允许,之前已经说过这一点)。
看结果
- 网关收到多少:
curl 127.0.0.1:18101/metrics,按租户和协议分别统计请求数、条数、字节数 - 实际存进去多少:搜索
service.name:load-test | stats count(),和发送端的数字对比,必须一致 - 磁盘占用:
docker exec coding-victorialogs-1 du -sh /vlogs - CPU 和内存:
docker stats --no-stream
今天的初步数据(仅供参考)
| 场景 | 结果 | 说明 |
|---|---|---|
| OTLP/gRPC,telemetrygen,6 个 worker,15 秒 | 约 35.9 万条/秒,发 538.2 万条,存 538.2 万条 | telemetrygen 的每条日志很短,所以条数高、字节数低;压测工具、网关和 VictoriaLogs 挤在同一台 6 核机器上 |
| HEC,hec_replay.py 模拟 nginx 日志 | 约 8.9 万条/秒(20 MB/s),30 万条全部存入 | 瓶颈在 Python 发送端,不在网关 |
| 经 OTel Collector 转发(gzip) | 发 4959 条,存 4959 条 | 功能验证,没有压测 |
| 网关内存 | 约 27 MB | 压测之后的读数 |
这些数字只能说明链路是通的,并且网关不是瓶颈,不能拿去对外宣传。要得到正式数字,需要把压测工具放到另一台机器上,用真实日志测。
4. OpenTelemetry 怎么接
OpenTelemetry 现在很流行,所以 OTLP 和 HEC 一样,是网关的一等入口。客户一般有三种接法:
方式 A:应用 SDK 直接发(最简单)
几乎所有语言的 OTel SDK 都认这几个标准环境变量,不用改代码:
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://你的域名/v1/logs
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer%20<key>
OTEL_SERVICE_NAME=checkout
方式 B:OTel Collector 做采集代理(生产推荐)
应用先发给本机或集群内的 Collector,由 Collector 负责批量、压缩、重试和排队,再转发给我们。用 filelog 采集日志文件也是走这一路。示例配置见 deploy/otel-collector.yaml,关键部分如下:
receivers:
filelog: # 采集日志文件
include: [/var/log/app/*.log]
otlp: # 接收应用 SDK 发来的数据
protocols: { grpc: {}, http: {} }
processors:
batch: { send_batch_size: 2000, timeout: 1s }
exporters:
otlphttp/osmanthe:
endpoint: https://你的域名 # 会自动拼上 /v1/logs
compression: gzip
headers: { authorization: "Bearer <key>" }
service:
pipelines:
logs: { receivers: [filelog, otlp], processors: [batch], exporters: [otlphttp/osmanthe] }
方式 C:Kubernetes
用官方 Helm chart 把 Collector 部署成 DaemonSet,在每个节点上采集容器日志,exporter 的写法和方式 B 一样。
OTLP 字段怎么存
| OTLP 字段 | 存成 |
|---|---|
| body | _msg(结构化的 body 会转成 JSON 文本) |
| timeUnixNano,没有就用 observedTimeUnixNano | _time,纳秒精度 |
| resource 和 log 的 attributes | 原样作为字段,比如 service.name、http.status_code;嵌套的 map 用点号展开 |
| severityText / severityNumber | severity(只有数字时自动换算成 INFO、ERROR 等)、severity_number |
| traceId / spanId | trace_id / span_id(十六进制),以后可以和链路追踪打通 |
| service.name、host.name、k8s.namespace.name 等 | 作为 VictoriaLogs 的 stream 字段,按服务和主机过滤会更快;在配置文件 [otlp].stream_fields 里修改 |
5. Splunk 客户怎么迁移
采集端只改地址和 token,其他都不用动:
# Fluent Bit
[OUTPUT]
Name splunk
Host 你的域名
Port 443
TLS On
Splunk_Token <key>
# Vector
[sinks.osmanthe]
type = "splunk_hec_logs"
endpoint = "https://你的域名"
default_token = "<key>"
encoding.codec = "json"
HEC 的 event、raw、health 三个接口都实现了,错误码和 Splunk 一致(比如 2 表示缺少 token,4 表示 token 无效,6 表示数据格式错误)。目前还不支持 indexer acknowledgement(useACK)。
6. 拿到客户真实数据后怎么测
- 让客户给一份脱敏的压缩样本,最好是 1 到 10 GB 的 zst 或 gz 文件。
- 用
hec_replay.py流式灌进去。如果客户用的是 OTel,就用 Collector 的 filelog 去读这份文件。 - 记录下面这张表,它会直接用于定价和容量规划:
| 指标 | 怎么得到 | 用来做什么 |
|---|---|---|
| 压缩比 | 原始字节数 ÷ /vlogs 的磁盘占用 | 算每 GB 的存储成本,进而定价 |
| 写入吞吐 | 回放脚本的 MB/s,加上 /metrics | 每台机器能承载多少客户 |
| 客户常用查询的延迟 | 搜索接口返回的 took_ms | 体验是否比 Splunk 好 |
| 高基数字段查询 | 按 trace_id、user_id 精确查找 | 排障场景是否够快 |
| 是否丢数据 | 发送条数和 stats count() 对比 | 必须为 0 |
7. 还没做的和风险
- 写入缓冲:现在每个请求同步写入 VictoriaLogs。VictoriaLogs 短暂不可用时由客户端重试,网关本身没有本地队列(WAL)。
- 限流和配额:还没做。一个租户猛发数据会影响其他租户。这是多租户 SaaS 必须补的。
- API Key:目前明文写在配置文件里,示例 Key 必须改掉。以后要改成哈希存储,并加上管理接口。
- 查询语言:直接用 LogsQL。SPL 兼容和自然语言查询放到后面做。
- 公网入口:见第 2 节,需要单独开放 HEC 和 OTLP 的入口。
- 存储保护:VictoriaLogs 设置了数据保留 7 天,磁盘剩余不足 5 GB 时停止写入,防止把 VPS 写满。
- 运行超过 24 小时的容器会被系统自动停止,到时重新执行
up -d即可,数据在命名卷里,不会丢。