观测与调试
这些执行器记录查询、导出指标或提供调试与测试辅助;应评估热路径开销和数据保留策略。
learn_domain
实验性插件(v1.2.0 引入)
learn_domain 在 v1.2.0 首次发布,目前处于实验阶段:过滤条件、写入触发时机与配套 dynamic_domain_set 的接口可能在后续版本调整。生产部署前请评估学习速率与磁盘写入对持久化层的影响。
作用
观察 DNS 流水线中的请求域名,并把命中的 qname 写入指定 dynamic_domain_set。它只产生副作用,不修改请求或响应。
配置示例
- tag: learned_allow
type: dynamic_domain_set
args:
path: "/etc/oxidns/learned-allow.txt"
- tag: learn_allow
type: learn_domain
args:
provider: learned_allow
phase: after
questions: first
qtypes: ["A", "AAAA"]
success_only: true
answer_required: true
rule_kind: full
async: true
error_mode: continue
timeout: 1s
配置项
provider
- 类型:
string;必填:是 - 作用:引用目标
dynamic_domain_setprovider。 - 约束:必须是
dynamic_domain_set,不能引用普通domain_set。
phase
- 类型:
string;必填:否;默认值:after - 可选值:
before、after - 作用:
before:在后续 executor 执行前学习 request question。after:先执行后续链路,再根据响应结果决定是否学习。
questions
- 类型:
string;必填:否;默认值:first - 可选值:
first、all - 作用:控制学习第一个 question 或所有 question。
qtypes
- 类型:
array;必填:否;默认值:["A", "AAAA"] - 作用:只学习指定 DNS 查询类型。
success_only
- 类型:
bool;必填:否;默认值:true - 作用:仅
phase: after生效,要求响应 RCODE 为NOERROR。
answer_required
- 类型:
bool;必填:否;默认值:true - 作用:仅
phase: after生效,要求响应包含 answer。
rule_kind
- 类型:
string;必填:否;默认值:full - 可选值:
full、domain - 作用:控制学习到
dynamic_domain_set的规则类型。
async
- 类型:
bool;必填:否;默认值:true - 作用:控制是否只入队后继续执行。设为
false时会等待 provider 写入完成。
error_mode
- 类型:
string;必填:否;默认值:continue - 可选值:
continue、stop、fail - 作用:控制学习失败后的执行结果。
timeout
- 类型:
duration;必填:否;默认值:1s - 作用:仅
async: false时用于限制等待 provider 写入的时间。
行为说明
- 学习域名会规范化为小写并移除尾部
.。 - 默认生成
full:example.com精确规则,避免意外扩大匹配范围。 phase: after默认只学习成功且有 answer 的A/AAAA响应。phase: before不检查响应条件。- v1 只学习请求 question 的 qname,不学习 CNAME target。
典型用途
- 把成功解析过的域名加入动态放行列表。
- 把某个分支中的域名沉淀到本地文件,供后续
qname $learned_allow或父级domain_set使用。
query_summary
作用
在后续链路执行完后输出紧凑查询摘要。
配置示例
- tag: summary_main
type: query_summary
args:
# 日志标题;便于区分不同链路
msg: "main pipeline"
配置项
msg
- 类型:
string;必填:否;默认值:"query summary" - 作用:定义摘要日志标题。
quick setup
- exec: "query_summary main"
行为说明
- 正向阶段记录开始时间。
- 回程阶段输出:
- 来源地址
- qname
- qtype
- rcode
- elapsed_ms
典型用途
- 低成本链路追踪。
- 分支延时对比。
query_recorder
作用
把入口 request、执行后 response 以及 sequence 路径事件持久化到 recorder 自己的 SQLite 数据库,并暴露历史查询、统计和 SSE 实时推送接口。
配置示例
- tag: query_recorder_main
type: query_recorder
args:
# SQLite 文件路径;多个 recorder 可以共用同一文件
path: "./data/query-recorder-main.sqlite"
# 热路径入队缓冲大小
queue_size: 8192
# 后台批量写入条数
batch_size: 256
# 后台批量 flush 间隔,单位毫秒
flush_interval_ms: 200
# 内存中保留多少条最近记录,供 SSE tail 回放
memory_tail: 1024
# 日志保留天数;最小 1
retention_days: 7
# 定时清理周期,单位小时;最小 1
cleanup_interval_hours: 1
# SQLite 读取侧最大并发数;最小 1
reader_concurrency: 2
配置项
path
- 类型:
string;必填:是 - 作用:指定当前 recorder 的 SQLite 文件路径。
queue_size
- 类型:
integer;必填:否;默认值:8192 - 作用:定义热路径到后台写线程的有界队列大小。
batch_size
- 类型:
integer;必填:否;默认值:256 - 作用:定义后台批量写入 SQLite 的单批记录数。
flush_interval_ms
- 类型:
integer;必填:否;默认值:200 - 作用:定义后台写线程的批量 flush 间隔。
memory_tail
- 类型:
integer;必填:否;默认值:1024 - 作用:定义最近记录的内存 tail 长度,用于
stream?tail=n回放。
retention_days
- 类型:
integer;必填:否;默认值:7 - 最小值:
1 - 作用:定义日志保留天数;过期数据会被定时实际删除。
cleanup_interval_hours
- 类型:
integer;必填:否;默认值:1 - 最小值:
1 - 作用:定义过期清理任务的执行周期。
reader_concurrency
- 类型:
integer;必填:否;默认值:2 - 最小值:
1 - 作用:限制 WebUI / API 统计与历史查询同时运行的 SQLite reader 数量,避免突发读取在大库上占用过多阻塞线程和内存。
行为说明
- 这是纯 executor 观察器,不会修改
server收口逻辑。 - 进入时抓取入口 request 的结构化快照,并启用
DnsContext.execution_path。 next返回后立即提交记录;成功时记录当前 response,失败时记录error和空 response。- request / response 不保存 wire 数据,而是把问题区、RR、EDNS 等字段拆成 JSON 文本列。
client_ip字段来自 OxiDNS 看到的传输层来源地址;如果前面有 systemd-resolved、dnsmasq、AdGuardHome、dae、clash 等本机转发链路,记录中可能只看到127.0.0.1。- 每个 recorder 使用同一前缀下的版本化表:
qr_<safe_tag>_<fnv64hex>_v1_recordsqr_<safe_tag>_<fnv64hex>_v1_stepsqr_<safe_tag>_<fnv64hex>_v1_questionsqr_<safe_tag>_<fnv64hex>_v1_meta
records主表只保存固定字段集合;steps表保存sequence路径事件,用于执行路径分析和命中率统计;questions表是从questions_json派生的索引表,用于加速 qname/qtype 查询;meta表记录已完成的派生表迁移。- 每个 recorder 独占自己的有界队列、SQLite 连接、后台写线程、内存 tail 和 SSE 广播器。
- 共用同一
path的 recorder 会按规范化文件路径协调 reader、writer 和维护操作;表和内存状态仍按 tag 隔离。 - 定时保留期清理会删除过期记录、回收全部完整空闲页,并在最后截断 WAL。只有删除产生完整空闲页时,数据库文件才会实际变小;少量删除可能只释放页内空间供后续写入复用。
- 旧数据库若仍是
auto_vacuum=NONE,第一次定时清理或手动清空会执行一次完整VACUUM,迁移到INCREMENTAL模式。该过程可能短暂停止 recorder 的数据库读写,并需要额外临时磁盘空间,但不会阻塞 DNS 请求处理。 - 维护失败不会终止后台 writer;定时任务会在后续周期重试,并通过结构化日志报告回收前后的页数、文件大小和失败阶段。
数据结构约定
questions_json固定为 question 数组,例如:
[
{ "name": "www.example.com.", "qtype": "A", "qclass": "IN" }
]
answers_json、authorities_json、additionals_json、signature_json固定为 RR 数组,例如:
[
{
"name": "www.example.com.",
"class": "IN",
"ttl": 300,
"rr_type": "A",
"payload_kind": "A",
"payload_text": "192.0.2.1",
"payload": { "ip": "192.0.2.1" }
}
]
req_edns_json、resp_edns_json固定为 EDNS 对象或NULL。- 表名中的
v1是当前 schema 版本;后续升级会新增新版本表,不做原地改表。
API
GET /plugins/<tag>/records- 按
created_at_ms倒序分页返回主表记录。 - 查询参数:
cursor=<created_at_ms>:<id>limit=<n>,默认100,最大500since_ms=<unix_ms>until_ms=<unix_ms>qname=<text>,按请求问题名包含匹配client_ip=<text>,按客户端 IP 字符串包含匹配qtype=<type>/rcode=<rcode>/status=all|error|has_response|no_response
- 按
GET /plugins/<tag>/records/<id>- 返回单条完整记录,并附带
steps。
- 返回单条完整记录,并附带
DELETE /plugins/<tag>/records- 清空当前 recorder 的所有历史记录和
steps,并清空内存 tail。 - 操作会先 flush 后台写入队列,再用小批次自动提交删除记录;每批删除和每批页面回收后都会截断 WAL,避免一个覆盖整次清空的大事务导致 WAL 持续增长。返回
cleared_records表示删除的主表记录数。 - 若历史已删除但磁盘回收失败,接口返回
query_recorder_clear_failed,错误信息会明确说明历史已经清除;后续维护仍可重试空间回收。
- 清空当前 recorder 的所有历史记录和
GET /plugins/<tag>/stats/plugins- 返回按
matcher / executor / builtin聚合的命中统计。 - 支持
since_ms、until_ms、kind=matcher|executor|builtin|all和 records 的过滤参数。
- 返回按
GET /plugins/<tag>/stats/top_clients/stats/top_qnames- 返回客户端 IP 或 QNAME 排行。
- 支持
limit=<n>,默认20;后端不再强制200上限。 - 支持 records 的时间范围与过滤参数。
GET /plugins/<tag>/stats/qtype/stats/rcode- 返回 QTYPE 或 RCODE 分布。
- 支持 records 的时间范围与过滤参数。
GET /plugins/<tag>/stats/latency- 返回延迟摘要、直方图和慢查询排行。
- 支持
slow_limit=<n>或limit=<n>,默认20;后端不再强制200上限。
GET /plugins/<tag>/stats/timeseries- 返回按分钟或小时聚合的查询趋势。
- 支持
bucket=minute|hour和buckets=<n>(默认60,最大720)。
GET /plugins/<tag>/stream- 以 SSE 实时推送新写入记录。
- 支持
tail=<n>回放最近n条内存 tail。 - 客户端应发送
Accept: text/event-stream,并容忍 heartbeat、错误事件、空 payload 和短暂断连。
典型用途
- 持久化审计和问题排查。
- 分析
sequence执行路径、插件命中率和异常分支。 - 给控制面或外部面板提供实时查询日志流。
注意事项
- 想要拿到完整主链路路径,推荐把 recorder 放在入口附近。
- 如果前置分支在 recorder 之前就短路返回,该请求不会被当前 recorder 记录。
- 若
next报错而server后续补发了兜底响应,数据库里仍只记录插件视角下的error和空 response。 - 若未启用管理 API,recorder 仍会写库,但不会暴露查询与 SSE 路由。
- 若需要真实终端 IP,请让客户端直连 OxiDNS,或在 HTTP/DoH 反向代理场景配置可信的
src_ip_header。
metrics_collector
作用
收集轻量级请求计数与延时指标,并导出 Prometheus 格式。
配置示例
- tag: metrics_main
type: metrics_collector
args:
# 指标名称标签;导出到 /api/metrics 时会带上它
name: "main"
配置项
name
- 类型:
string;必填:否;默认值:"default" - 作用:定义当前指标收集器的名称标签。
quick setup
- exec: "metrics_collector main"
行为说明
- 正向阶段:
query_total +1inflight +1- 记录开始时间
- 回程阶段:
inflight -1- 若无响应则
err_total +1 - 若有响应则累计延时
API
GET /api/metrics- Prometheus 文本格式,全局唯一;其它插件的内置 metrics 也会汇总到同一接口。
典型用途
- 主链路观测。
- 多条策略入口延时对比。
debug_print
作用
打印请求与响应对象,便于调试。
配置示例
- tag: debug_main
type: debug_print
args:
# 日志标题;未配置时默认是 "debug print"
msg: "before forward"
msg- 可选日志标题。
- 默认
"debug print"。
配置项
msg
- 类型:
string;必填:否;默认值:"debug print" - 作用:定义日志输出标题。
quick setup
- exec: "debug_print cache branch"
典型用途
- 排查 sequence 分支。
- 验证请求和响应在插件前后是否被改写。
sleep
作用
异步延迟,用于测试和策略实验。
配置示例
- tag: sleep_100ms
type: sleep
args:
# 额外异步延迟 100ms
duration: 100
配置项
duration
- 类型:
integer;必填:否;默认值:0 - 单位:毫秒
- 作用:定义当前请求在该执行器上的额外异步等待时间。
quick setup
- exec: "sleep 100"
典型用途
- 测试
fallback阈值。 - 人工制造慢路径验证观测链路。