- CheckResult: statusDetail -> observation (持久化) + detail (API 动态派生) - 存储: status_detail 列 -> observation TEXT (JSON) - CheckerDefinition: 新增 buildDetail(observation) 方法 - 各 checker 返回结构化 observation,API 层通过 registry 调用 buildDetail - HTTP: bodyPreview 在 status/header 失败时也提前采集 - UDP: observation 包含 durationMs,未响应归为 error failure - CMD: 超时/输出超限时保留已收集 observation - TCP: connectTimeMs 仅含连接建立耗时,不含 banner 等待 - 新增 buildDetail 单测和 mapCheckResult 覆盖测试 - 同步 openspec 主规范,归档 checker-observation 变更
DiAL
轻量级多类型拨测监控工具
基于 Bun + TypeScript 构建 · YAML 配置驱动 · 内置 Dashboard
DiAL 是一个自托管的拨测监控工具,支持 HTTP、命令行、数据库、TCP、UDP、Ping 和 LLM 多种拨测类型。通过 YAML 配置文件定义拨测目标,后端定时并发执行拨测并将结果持久化到本地 SQLite,前端 Dashboard 展示各目标的实时状态、可用率和耗时趋势。
功能亮点:
- 多种拨测类型:HTTP(GET/POST/PUT 等)、Cmd(命令行执行)、DB(PostgreSQL/MySQL/SQLite)、TCP(端口可达性 + Banner 探测)、UDP(自定义 payload 请求-响应)、Ping(ICMP 存活、延迟、丢包率)、LLM(大模型服务应用层健康检查)
- 丰富的校验规则:状态码、响应头、JSONPath、CSS 选择器、XPath、正则匹配、数值比较等
- 结构化观测数据:检查结果保留 HTTP body 预览、TCP/UDP 响应摘要、Ping 丢包率、CMD 输出预览、LLM token 用量等 observation,便于排障和后续分析
- 响应式 Dashboard:实时状态、可用率统计、耗时趋势图、手动/自动刷新
- 多主题支持:系统、明亮、黑暗三种主题模式
- 零外部依赖:数据存储使用 SQLite,无需额外数据库服务
快速开始
前置条件: Bun >= 1.0
Ping checker 依赖系统 ping 命令。精简容器镜像需额外安装,例如 Alpine 可安装 iputils-ping。
# 克隆仓库
git clone https://github.com/your-org/DiAL.git
cd DiAL
# 安装依赖
bun install
# 复制示例配置并按需修改
cp probes.example.yaml probes.yaml
# 启动开发服务器
bun run dev probes.yaml
bun run dev 会同时启动 Vite 开发服务器(http://127.0.0.1:5173)和 API 服务器(http://127.0.0.1:3000),访问前端地址即可使用 Dashboard。
生产部署
# 构建
bun run build
# 运行
./dist/dial-server ./probes.yaml
构建产物为独立可执行文件,只需一个 YAML 配置文件即可运行。
配置文件
程序通过 YAML 配置文件定义所有运行参数,完整示例参见 probes.example.yaml。
# yaml-language-server: $schema=./probe-config.schema.json
server:
host: "127.0.0.1"
port: 3000
dataDir: "/tmp/probes_data"
runtime:
maxConcurrentChecks: 20
retention: "7d"
variables:
env_name: "生产"
base_url: "https://api.example.com"
api_token: "Bearer demo-token"
sqlite_url: "sqlite:///path/to/db.sqlite"
defaults:
interval: "30s"
timeout: "10s"
http:
maxBodyBytes: "10MB"
cmd:
maxOutputBytes: "1MB"
targets:
- id: "baidu-home"
name: "Baidu"
type: http
http:
url: "https://www.baidu.com"
expect:
status: [200]
durationMs:
lte: 10000
- id: "json-api"
name: "${env_name} JSON API 示例"
type: http
http:
url: "${base_url}/json"
headers:
Authorization: "${api_token|Bearer fallback-token}"
expect:
status: [200]
headers:
Content-Type:
contains: "application/json"
body:
- json:
path: "$.slideshow.title"
equals: "Sample Slide Show"
- id: "bun-script"
name: "Bun 脚本检查"
type: cmd
cmd:
exec: "bun"
args: ["-e", "console.log('ok')"]
expect:
exitCode: [0]
stdout:
- contains: "ok"
- id: "sqlite-active-users"
name: "SQLite 数据库检查"
type: db
db:
url: "${sqlite_url}"
query: "SELECT COUNT(*) as cnt FROM users WHERE status = 'active'"
expect:
durationMs:
lte: 5000
rowCount: { gte: 1 }
rows:
- cnt: { gte: 0 }
- id: "redis-port"
name: "Redis 端口可达"
type: tcp
tcp:
host: "127.0.0.1"
port: 6379
expect:
durationMs:
lte: 3000
- id: "udp-heartbeat"
name: "UDP 心跳检测"
type: udp
udp:
host: "127.0.0.1"
port: 9000
payload: "PING"
expect:
response:
- contains: "PONG"
durationMs:
lte: 100
- id: "gateway-ping"
name: "网关 ICMP 可达"
type: ping
ping:
host: "10.0.0.1"
count: 3
packetSize: 56
expect:
alive: true
packetLossPercent:
lte: 10
avgLatencyMs:
lte: 100
maxLatencyMs:
lte: 300
durationMs:
lte: 5000
配置说明
server — 服务配置(均可省略,使用默认值)
| 字段 | 说明 | 默认值 |
|---|---|---|
host |
监听地址 | 127.0.0.1 |
port |
监听端口 | 3000 |
dataDir |
数据目录,相对路径基于配置文件所在目录解析 | ./data |
runtime — 运行时配置
| 字段 | 说明 | 默认值 |
|---|---|---|
maxConcurrentChecks |
最大并发拨测数 | 20 |
retention |
历史数据保留时长,支持 ms/s/m/h/d 单位 |
7d |
defaults — 全局默认值(均可省略)
| 字段 | 说明 | 默认值 |
|---|---|---|
interval |
拨测间隔 | 30s |
timeout |
超时时间 | 10s |
http.maxBodyBytes |
响应体最大字节数 | 100MB |
http.headers |
默认请求头(target 中的 headers 会合并覆盖同名头) | — |
cmd.maxOutputBytes |
输出最大字节数 | 100MB |
cmd.cwd |
默认工作目录(相对于配置文件所在目录) | . |
variables — 配置变量(可省略)
variables 是顶层动态键值表,key 必须符合 [a-zA-Z_][a-zA-Z0-9_]*,value 仅支持 string、number、boolean。target 中的字符串值可引用变量:
${key}:引用 variables 或环境变量${key|default}:变量和环境变量都不存在时使用默认值,第一个|后的内容为默认值$${key}:转义输出字面量${key}
解析优先级为 variables -> process.env -> 默认值。字段值完整等于单个变量引用时会保留 number/boolean/string 类型;部分拼接时统一转为字符串。变量替换仅作用于 targets,且不会替换 id 和 type 字段。
targets — 拨测目标列表(必填)
每个 target 的通用字段:
| 字段 | 说明 | 必填 |
|---|---|---|
id |
目标唯一标识,最长 30 字符,支持字母数字、下划线、连字符,不参与变量替换 | 是 |
name |
展示名称,最长 30 字符,支持变量替换,可省略或显式 null;前端展示时 null 回退到 id |
否 |
description |
目标描述,最长 500 字符,支持变量替换,可省略或显式 null,允许空字符串 | 否 |
type |
目标类型:http、cmd、db、tcp、udp、ping、llm |
是 |
group |
分组名称 | 否,默认 "default" |
interval |
覆盖全局拨测间隔 | 否 |
timeout |
覆盖全局超时时间 | 否 |
HTTP 类型 (type: http)
| 字段 | 说明 |
|---|---|
http.url |
目标 URL |
http.method |
HTTP 方法(覆盖 defaults) |
http.headers |
请求头(与 defaults.http.headers 合并) |
http.body |
请求体 |
http.ignoreSSL |
忽略 HTTPS 证书校验,默认 false |
http.maxRedirects |
最大重定向跟随次数,默认 0(不跟随) |
Cmd 类型 (type: cmd)
| 字段 | 说明 |
|---|---|
cmd.exec |
可执行文件名或路径 |
cmd.args |
命令行参数列表 |
cmd.env |
环境变量覆盖(继承进程环境变量并合并) |
cmd.cwd |
工作目录(相对于配置文件所在目录) |
DB 类型 (type: db)
| 字段 | 说明 |
|---|---|
db.url |
数据库连接字符串,支持 postgres://、mysql://、sqlite:// |
db.query |
SQL 查询语句(不配置时仅测试连接) |
TCP 类型 (type: tcp)
| 字段 | 说明 |
|---|---|
tcp.host |
目标主机地址 |
tcp.port |
目标端口(1-65535) |
tcp.readBanner |
是否读取服务端 banner,默认 false |
tcp.bannerReadTimeout |
banner 读取超时(毫秒),默认 2000 |
tcp.maxBannerBytes |
banner 最大字节数,支持 KB/MB/GB 单位,默认 4KB |
Ping 类型 (type: ping)
| 字段 | 说明 |
|---|---|
ping.host |
目标主机地址 |
ping.count |
ICMP 包数量,默认 3,范围 1-100 |
ping.packetSize |
ICMP 包大小(bytes),默认 56 |
Ping checker 通过系统 ping 命令执行 ICMP 探测,支持 Linux、macOS 和 Windows 输出解析。
LLM 类型 (type: llm)
| 字段 | 说明 |
|---|---|
llm.provider |
模型提供方:openai、openai-responses、anthropic |
llm.url |
API base URL |
llm.model |
模型名称 |
llm.prompt |
单轮 prompt |
llm.mode |
调用模式:http(默认,非流式)或 stream(流式) |
llm.key |
API key(默认空字符串,支持 ${VAR} 变量替换) |
llm.authToken |
Bearer token(仅 anthropic provider,与 key 互斥) |
llm.headers |
附加请求头(与 defaults.llm.headers 合并) |
llm.ignoreSSL |
忽略 HTTPS 证书校验,默认 false |
llm.options |
生成选项(与 defaults.llm.options 合并) |
llm.providerOptions |
Provider 专属选项(与 defaults.llm.providerOptions 合并) |
llm.options 支持 maxOutputTokens(默认 16)、temperature(默认 0)、topP、topK、presencePenalty、frequencyPenalty、stopSequences、seed。
defaults.llm 支持 mode、headers、ignoreSSL、options、providerOptions,不支持 provider、url、model、key、authToken、prompt。
expect — 期望校验
| 字段 | 适用类型 | 说明 |
|---|---|---|
status |
HTTP/LLM | 可接受的状态码列表,支持精确码和范围(如 "2xx");默认 [200] |
exitCode |
Cmd | 可接受的退出码列表;未指定时默认 [0] |
headers |
HTTP/LLM | 响应头校验,使用动态键名和 KeyValueExpect |
durationMs |
全部 | 完整执行耗时校验,使用 ValueMatcher,如 { lte: 1000 } |
output |
LLM | 模型输出校验,使用 ContentRules 数组 |
finishReason |
LLM | finish reason 校验,使用 ValueMatcher |
rawFinishReason |
LLM | 原始 finish reason 校验,使用 ValueMatcher |
usage |
LLM | Token usage 校验(inputTokens/outputTokens/totalTokens matcher) |
stream |
LLM | 流式断言(completed、firstTokenMs matcher,仅 mode: stream) |
body |
HTTP | 响应体校验,使用 ContentRules 数组 |
stdout / stderr |
Cmd | 输出校验,使用 ContentRules 数组 |
rowCount |
DB | 查询返回行数校验,使用 ValueMatcher |
rows |
DB | 查询结果逐行校验,数组内每行为列名到 KeyValueExpect 的映射 |
result |
DB | 完整查询结果 { rows, rowCount } 校验,使用 ContentRules 数组 |
connected |
TCP | 期望连接结果,true(默认)可达或 false 期望不可达 |
banner |
TCP | Banner 内容校验,使用 ContentRules 数组,需开启 tcp.readBanner |
responded |
UDP | 期望是否收到响应,默认 true |
response |
UDP | 响应内容校验,使用 ContentRules 数组 |
responseSize |
UDP | 响应字节数校验,使用 ValueMatcher |
sourceHost |
UDP | 响应来源地址校验,使用 ValueMatcher |
sourcePort |
UDP | 响应来源端口校验,使用 ValueMatcher |
alive |
Ping | 期望主机可达性,默认 true |
packetLossPercent |
Ping | 丢包率百分比校验,范围 0-100,使用 ValueMatcher |
avgLatencyMs |
Ping | 平均延迟校验,使用 ValueMatcher |
maxLatencyMs |
Ping | 最大单次延迟校验,使用 ValueMatcher |
ContentRules 校验项(body、stdout、stderr、banner、response、output、result 均使用数组):
contains— 响应体包含指定文本regex— 正则匹配(启动期会拒绝存在 ReDoS 风险的模式)json— JSONPath 提取值比较(path必填,如$.slideshow.title)css— CSS 选择器提取 HTML 元素(selector必填,attr可选提取属性)xpath— XPath 提取 XML/HTML 节点(path必填,如/html/body/h1/text())
ValueMatcher 字段:equals、contains、regex、empty、exists、gte、lte、gt、lt。equals 支持 JSON 深度相等;regex 固定使用无 flags 正则;提取器未配置 matcher 时等价于 exists: true。ValueMatcher expect 字段也可直接写 string、number、boolean 或 null,等价于 { equals: value };数组和对象必须显式写成 { equals: ... }。
旧字段 maxDurationMs、maxPacketLoss、maxAvgLatencyMs、maxMaxLatencyMs 和旧正则字段 match 已移除,请分别改用 durationMs、Ping matcher 字段和 regex。
大小说明:maxBodyBytes 和 maxOutputBytes 支持 KB、MB、GB 单位,也可直接使用数字。
时长格式:500ms、30s、5m、2h、7d
JSON Schema:仓库根目录导出 probe-config.schema.json,在 YAML 文件顶部添加 # yaml-language-server: $schema=./probe-config.schema.json 即可在编辑器中获得提示和校验。
注意: 配置校验在启动时执行,非法配置会阻止启动并输出错误信息。除动态键值表(
headers、env、variables)外,未知字段会导致启动失败,请使用 YAML 注释。
目标状态判定
采用单层判定模型:
- UP = 拨测结果符合 expect 规则
- DOWN = 拨测结果不符合 expect 规则
执行失败(网络错误、超时、进程崩溃)和 expect 不匹配都统一为 DOWN,通过 failure.kind 区分原因("error" vs "mismatch")。
API 返回的检查结果包含 detail 和 observation:detail 是后端按 checker 类型从结构化 observation 动态生成的人可读摘要,observation 保存该次检查的结构化观测数据。detail 不写入 SQLite,存储层仅持久化 observation JSON、failure JSON、匹配状态、耗时和时间戳。
开发
bun run check # schema:check + typecheck + lint + test
bun run verify # check + build
开发相关文档(项目结构、构建、测试、代码规范等)请参阅 DEVELOPMENT.md。
License
MIT