Kibitzr 8.0.0 配置参数详解

逐组实测 Kibitzr 8.0.0 的 checks、数据源、period/schedule 调度、CSS/XPath/Jinja/changes/jq 变换、notify/creds/templates/batch 等配置参数,记录 verify-cert 重试缺陷、creds 不插值、重复名不校验等与文档不符项及规避方法,附两套生产配置模板。

KibitzrKibitzrPython 编程监控Web 监控自动化运维2026/10/27

Kibitzr 8.0.0 配置参数详解

目标:把 Kibitzr 8.0.0 的 kibitzr.yml 配置拆成可逐项验证的参数组,在 Ubuntu 22.04 服务器上对 checks、数据源、调度、变换、通知、凭据、模板复用、日志级别逐组实测,记录真实回显与「文档与实际不符」的坑位,给出可直接套用的生产配置模板。文中全部命令与输出均为 2026-10-27 真机实测。

一、环境信息

项目实测值
操作系统Ubuntu 22.04.5 LTS
架构x86_64
Python3.10.12
Kibitzr8.0.0(/opt/kibitzr/venv)
系统依赖git 2.34.1、jq 1.6
配置文件/opt/kibitzr/kibitzr.yml
凭据文件/opt/kibitzr/kibitzr-creds.yml
运行用户kibitzr(uid 999)
测试 HTTP 站点http://127.0.0.1:8131(index.html、list.html、changed.txt、data.json)
测试 HTTPS 站点https://127.0.0.1:8443(自签证书)

测试站点的四个文件内容如下,后文的参数实验都基于它们:

$ curl -s http://127.0.0.1:8131/index.html
<html><head><title>Demo Site</title></head><body><div id="status">BUILD-2026-10-27-02</div><p>hello kibitzr</p></body></html>

$ cat /srv/www/list.html
<html><body>
<div class="item">Alpha</div>
<div class="item">Beta</div>
<section id="main"><p>Hello <b>World</b></p></section>
</body></html>

$ cat /srv/www/changed.txt
VERSION=4

$ cat /srv/www/data.json
{"name":"kibitzr","version":8,"tags":["monitor","python"]}

💡 说明:Kibitzr 的所有参数都写在 YAML 里,没有命令行参数可以覆盖单个检查项,也没有 --config 选项指定配置文件路径。因此下文每个参数组都用一个独立工作目录 + 独立 kibitzr.yml 验证,互不干扰。

二、前置条件

2.1 已安装 Kibitzr 与可选依赖

/opt/kibitzr/venv/bin/kibitzr version
git --version
jq --version
8.0.0
git version 2.34.1
jq-1.6

changes 变换依赖系统 git,jq 变换依赖系统 jq,两者都不在 pip 依赖里。缺少时不是「功能降级」而是直接抛异常,详见第七节。

2.2 工作目录即配置目录

Kibitzr 只从当前工作目录和用户目录查找配置。在空目录执行会得到完整的查找路径清单(输出节选,实测省略中间调用栈):

mkdir -p /opt/kibitzr/work/case_nocfg
cd /opt/kibitzr/work/case_nocfg
/opt/kibitzr/venv/bin/kibitzr once
Traceback (most recent call last):
  File "/opt/kibitzr/venv/bin/kibitzr", line 6, in <module>
    sys.exit(extended_cli())
  ...
  File "/opt/kibitzr/venv/lib/python3.10/site-packages/kibitzr/conf.py", line 43, in detect_config_dir
    raise ConfigurationError(
kibitzr.exceptions.ConfigurationError: kibitzr.yml not found in following locations: kibitzr.yml, /root/.config/kibitzr/kibitzr.yml, /root/kibitzr.yml

查找顺序因此固定为:./kibitzr.yml → ~/.config/kibitzr/kibitzr.yml → ~/kibitzr.yml。同理,pages/(变更历史)与 stash.db(键值存储)也都是相对当前工作目录生成的,切换目录等于切换一整套状态。

⚠️ 注意:kibitzr --help 的选项只有 -l/--log-level 与 --help,没有 --config。想让 Kibitzr 读某个配置文件,只能 cd 到该文件所在目录,或用 systemd 的 WorkingDirectory= 固定目录。

三、参数查看与修改方式

3.1 查看当前生效的配置文件路径

配置文件没有专门的「show config」子命令,最可靠的方式是把日志级别调到 debug,它会打印实际加载的路径:

cd /opt/kibitzr/work/case_ext
/opt/kibitzr/venv/bin/kibitzr -l debug once

输出节选(实测首部):

2026-10-27 14:51:26,416 [DEBUG] kibitzr.conf: Loading credentials from /opt/kibitzr/work/case_ext/kibitzr-creds.yml
2026-10-27 14:51:26,416 [INFO] kibitzr.conf: No credentials file found at /opt/kibitzr/work/case_ext/kibitzr-creds.yml
2026-10-27 14:51:26,418 [DEBUG] kibitzr.conf: Loading settings from /opt/kibitzr/work/case_ext/kibitzr.yml
2026-10-27 14:51:26,441 [INFO] kibitzr.fetcher.loader: Fetching From template at http://127.0.0.1:8131/changed.txt

Loading settings from /opt/kibitzr/work/case_ext/kibitzr.yml 这类行就是最终生效的配置文件;同时 debug 还暴露了 HTTP 缓存、urllib3 连接、通知器发出的原始报告,排障时非常有用。

3.2 修改后的生效方式

常驻模式(kibitzr run)不会自动感知文件变化,改完 kibitzr.yml 需要重载进程。官方提供 kibitzr reload,它确实能定位 systemd 托管的主进程并发 SIGUSR1,但命令自身也会收到同一信号而退出:

cd /opt/kibitzr
/opt/kibitzr/venv/bin/kibitzr reload
2026-10-27 14:57:10,322 [INFO] kibitzr.app: Send singal SIGUSR1 to process: 5945
User defined signal 1
exit=138

服务端日志证明重载指令已生效:

Oct 27 14:57:10 kibitzr-lab kibitzr[5945]: 2026-10-27 14:57:10,322 [INFO] kibitzr.app: Received SIGUSR1. Flagging configuration reload

因此在脚本里它会被判定为失败(退出码 138)。更稳妥的做法是执行 systemctl restart kibitzr,或精确对主进程发信号:

kill -USR1 "$(systemctl show -p MainPID --value kibitzr)"
echo $?
0

💡 说明:reload 的退出码 138 是 128 + 10(SIGUSR1),属于命令自身被信号终止,不是重载失败。判断重载是否成功要看服务端日志里的 Received SIGUSR1. Flagging configuration reload。

四、参数总览

参数作用实测结论
checks检查项列表(顶层键)必填,列表元素为映射
name检查名可省略,省略时自动命名为 http-127-0-0-1-8131-index-html 形式;重复名不报错
urlHTTP 数据源与 script 二选一,都缺失直接抛 ConfigurationError
scriptShell / Python 数据源支持字符串(shell)与 python: 映射
delay浏览器渲染等待秒数走 Selenium,缺驱动直接失败
verify-cert是否校验 HTTPS 证书false 可关闭校验;默认校验自签证书会失败
period周期秒数或人类可读时长15、2 hours、37 minutes 均被接受
schedule日历调度支持单条与列表,日志会逐条打印调度计划
transform变换流水线支持 text/css/css-all/xpath/tag/json/jinja/changes/jq/shell 等
notify通知器列表支持字符串项与映射项;空报告不通知
pageschecks 的历史别名实测可用,兼容旧配置
templates配置模板(顶层键)通过 template: 引用,合并字段
batch / url-pattern / items批量生成检查{} 占位替换,实测可用
notifiers预定义通知器(顶层键)通过 python: 名称 引用
-l/--log-level日志级别仅 `debug

五、核心参数分类详解

5.1 checks 与 name:省略与重名

checks 是顶层列表,每个元素是一个检查。name 不是必填项,省略后 Kibitzr 用 URL 生成名字:

checks:
  - url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify:
      - python: print("OK:"+content[:15])
  - url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify:
      - python: print("OK2:"+content[:15])
$ /opt/kibitzr/venv/bin/kibitzr once
2026-10-27 14:51:11,589 [INFO] kibitzr.fetcher.loader: Fetching http-127-0-0-1-8131-index-html at http://127.0.0.1:8131/index.html
2026-10-27 14:51:11,613 [INFO] kibitzr.notifier.custom: Executing Python notifier
2026-10-27 14:51:11,613 [INFO] kibitzr.fetcher.loader: Fetching http-127-0-0-1-8131-index-html at http://127.0.0.1:8131/index.html
2026-10-27 14:51:11,615 [INFO] kibitzr.notifier.custom: Executing Python notifier
OK:Demo Site
BUILD
OK2:Demo Site
BUILD

两个检查都正常执行,但日志里名字完全相同,无法区分。重复的显式 name 同样不会被校验:

checks:
  - name: Same
    url: http://127.0.0.1:8131/index.html
    notify: [python: print(content)]
  - name: Same
    url: http://127.0.0.1:8131/index.html
    notify: [python: print(content)]
$ /opt/kibitzr/venv/bin/kibitzr once
2026-10-27 14:51:11,462 [INFO] kibitzr.fetcher.loader: Fetching Same at http://127.0.0.1:8131/index.html
2026-10-27 14:51:11,464 [INFO] kibitzr.notifier.custom: Executing Python notifier
2026-10-27 14:51:11,464 [INFO] kibitzr.fetcher.loader: Fetching Same at http://127.0.0.1:8131/index.html
2026-10-27 14:51:11,466 [INFO] kibitzr.notifier.custom: Executing Python notifier

⚠️ 注意:重复 name 不报错、不告警,但 pages/<name>/ 会被两个检查共用,一旦同时使用 changes 变换会互相覆盖历史。生产配置必须保证 name 唯一,建议用「业务-对象-指标」命名法(如 pay-api-version)。

5.2 数据源:url、script、delay

url 抓取 HTTP(S) 页面;script 从命令输出或 Python 变量取值;两者都没有时直接失败:

checks:
  - name: No source
    transform: [text]
    notify: [python: print(content)]
$ /opt/kibitzr/venv/bin/kibitzr once
kibitzr.exceptions.ConfigurationError: No fetcher is applicable for "{conf["name"]}"

这条报错把未插值的源码片段 {conf["name"]} 原样打印出来,看不出是哪个检查出错。检查数据源缺失时,请结合 -l debug 的加载路径与配置顺序人工定位。

script 支持两种形态,shell 用字符串,Python 用 python: 映射:

checks:
  - name: Shell fetch
    script: |
      echo "SCRIPT-OK"
    notify:
      - python: print("FETCH=" + content.strip())
  - name: Python fetch
    script:
      python: |
        content = "PY-" + conf["name"]
        ok = True
    notify:
      - python: print("FETCH=" + content)
2026-10-27 14:51:12,764 [INFO] kibitzr.fetcher.loader: Fetching 'Shell fetch' using script
2026-10-27 14:51:12,769 [INFO] kibitzr.fetcher.loader: Fetching 'Python fetch' using script
2026-10-27 14:51:12,769 [INFO] kibitzr.fetcher.script: Fetch using Python script
FETCH=SCRIPT-OK
FETCH=PY-Python fetch

Python 脚本必须给 content 赋值并把 ok 设为 True,否则本次视为失败、不进入变换与通知。

delay 会让抓取走 Selenium 浏览器路径。当前环境没有 Firefox 驱动,实测 40 秒后超时退出(输出节选,实测尾部):

checks:
  - name: Browser delay
    url: http://127.0.0.1:8131/list.html
    delay: 2
    transform: [text]
    notify:
      - python: print(content[:20])
$ timeout 40 /opt/kibitzr/venv/bin/kibitzr once
...
selenium.common.exceptions.NoSuchDriverException: Message: Unable to obtain driver for firefox; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location
exit=124

⚠️ 注意:delay 不是「等待 2 秒」这么简单,它会切换到浏览器抓取,需要可用的 Firefox 与 geckodriver,并由 selenium-manager 在线获取驱动。纯 HTTP 场景不要配置 delay;需要登录态或 JS 渲染时,先预装浏览器与驱动并验证驱动获取链路。

verify-cert 控制 HTTPS 证书校验。对自签证书默认校验会失败,关闭后可抓取成功:

checks:
  - name: TLS insecure
    url: https://127.0.0.1:8443/
    verify-cert: false
    transform: [text]
    notify:
      - python: print("INSECURE OK")
2026-10-27 14:52:12,231 [INFO] kibitzr.fetcher.loader: Fetching TLS insecure at https://127.0.0.1:8443/
/opt/kibitzr/venv/lib/python3.10/site-packages/urllib3/connectionpool.py:1129: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(
2026-10-27 14:52:12,255 [INFO] kibitzr.notifier.custom: Executing Python notifier
INSECURE OK

而开启校验(默认)时,抓取失败后的重试逻辑会触发 Python 3.10 不兼容代码:

$ /opt/kibitzr/venv/bin/kibitzr once "TLS strict"
  File "/opt/kibitzr/venv/lib/python3.10/site-packages/kibitzr/fetcher/simple.py", line 54, in sleep_on_exception
    if isinstance(seconds, collections.Callable):
AttributeError: module 'collections' has no attribute 'Callable'
exit=1

⚠️ 注意:这是 Kibitzr 8.0.0 在 Python 3.10 上的真实缺陷——collections.Callable 在 Python 3.10 已移除,抓取失败进入重试时必然抛 AttributeError,真正的证书错误被掩盖。规避方式:对内网自签站点配置 verify-cert: false;对公网站点保持默认校验,并在证书到期前更换证书,避免走到失败重试分支。

5.3 调度:period 与 schedule

period 支持纯秒数与人类可读时长,三种写法实测都被接受:

checks:
  - name: Period seconds
    url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify: [python: print(content)]
    period: 15
  - name: Period human
    url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify: [python: print(content)]
    period: 2 hours
  - name: Period minutes
    url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify: [python: print(content)]
    period: 37 minutes
2026-10-27 14:51:11,151 [INFO] kibitzr.fetcher.loader: Fetching Period seconds at http://127.0.0.1:8131/index.html
2026-10-27 14:51:11,176 [INFO] kibitzr.fetcher.loader: Fetching Period human at http://127.0.0.1:8131/index.html
2026-10-27 14:51:11,178 [INFO] kibitzr.fetcher.loader: Fetching Period minutes at http://127.0.0.1:8131/index.html
Demo Site
BUILD-2026-10-27-02
hello kibitzr
Demo Site
BUILD-2026-10-27-02
hello kibitzr
Demo Site
BUILD-2026-10-27-02
hello kibitzr

schedule 提供日历式控制,支持单条与列表,也支持把 every: day 写成单数:

checks:
  - name: Daily noon
    url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify: [python: print(content[:20])]
    schedule:
      every: 1
      unit: days
      at: "12:00"
  - name: Thursday
    url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify: [python: print(content[:20])]
    schedule:
      every: thursday
      at: "12:00"
  - name: Multi schedule
    url: http://127.0.0.1:8131/index.html
    transform: [text]
    notify: [python: print(content[:20])]
    schedule:
      - every: day
        at: "15:30"
      - every: hour

常驻模式启动时,调度计划会逐条打印:

$ timeout 4 /opt/kibitzr/venv/bin/kibitzr run
2026-10-27 14:53:34,073 [INFO] kibitzr.fetcher.loader: Fetching Daily noon at http://127.0.0.1:8131/index.html
2026-10-27 14:53:34,094 [INFO] kibitzr.fetcher.loader: Fetching Thursday at http://127.0.0.1:8131/index.html
2026-10-27 14:53:34,096 [INFO] kibitzr.fetcher.loader: Fetching Multi schedule at http://127.0.0.1:8131/index.html
2026-10-27 14:53:34,098 [INFO] kibitzr.timeline: Scheduling checks for 'Daily noon' every 1 days at '12:00'
2026-10-27 14:53:34,098 [INFO] kibitzr.timeline: Scheduling checks for 'Thursday' every 1 thursday at '12:00'
2026-10-27 14:53:34,098 [INFO] kibitzr.timeline: Scheduling checks for 'Multi schedule' every 1 day at '15:30'
2026-10-27 14:53:34,098 [INFO] kibitzr.timeline: Scheduling checks for 'Multi schedule' every 1 hour
2026-10-27 14:53:34,098 [INFO] kibitzr.app: Starting infinite loop

要点:

  • run 启动时会先立即执行一轮全部检查,再进入调度循环。
  • 一个检查可以配置多条 schedule,日志会为每条各打印一行。
  • period 与 schedule 同时配置时,两者都会生效,容易造成重复通知,生产配置二选一。
  • once 与 once NAME 只执行一轮,不等待调度时间。

💡 技术拓展:调度语义有三种常见实现——固定间隔(每 N 秒)、固定时刻(每天 12:00)、日历表达式(cron)。固定间隔实现简单但会漂移;固定时刻适合日报类;cron 表达力最强但可读性差。Kibitzr 用 period 覆盖固定间隔、用 schedule 覆盖固定时刻与星期,且不支持 cron 表达式,需要 cron 语义时请配合 systemd timer 或 crontab 在外部触发 kibitzr once。

5.4 变换:text、css、css-all、xpath、tag、json

transform 是按顺序执行的流水线,前一步的输出是后一步的输入。六种选择器实测:

checks:
  - name: T text
    url: http://127.0.0.1:8131/list.html
    transform:
      - text
    notify:
      - python: print("TEXT=" + repr(content))
  - name: T css
    url: http://127.0.0.1:8131/list.html
    transform:
      - css: "section#main"
      - text
    notify:
      - python: print("CSS=" + repr(content))
  - name: T cssall
    url: http://127.0.0.1:8131/list.html
    transform:
      - css-all: "div.item"
      - text
    notify:
      - python: print("CSSALL=" + repr(content))
  - name: T xpath
    url: http://127.0.0.1:8131/list.html
    transform:
      - xpath: "//section[@id=\"main\"]/p"
      - text
    notify:
      - python: print("XPATH=" + repr(content))
  - name: T tag
    url: http://127.0.0.1:8131/list.html
    transform:
      - tag: section
      - text
    notify:
      - python: print("TAG=" + repr(content))
  - name: T json
    url: http://127.0.0.1:8131/data.json
    transform:
      - json
    notify:
      - python: print("JSON=" + content)
TEXT='Alpha\nBeta\nHello\nWorld'
CSS='Hello\nWorld'
CSSALL='Alpha\nBeta'
XPATH='Hello\nWorld'
TAG='Hello\nWorld'
JSON={
  "name": "kibitzr",
  "tags": [
    "monitor",
    "python"
  ],
  "version": 8
}

逐项说明:

  • text 提取页面全部文本并压成多行字符串,噪声最多,通常要配合选择器。
  • css 取第一个匹配元素;css-all 取全部匹配元素并拼接。
  • xpath 与 CSS 等价能力,适合复杂层级;tag 按标签名取第一个。
  • json 把响应体解析成 Python 对象并格式化输出,后续可直接交给 jinja 或 jq。

5.5 变换:jinja

jinja 变换的上下文提供 env(环境变量)与 css() 等函数,并支持 Jinja 过滤器:

checks:
  - name: Jinja env
    url: http://127.0.0.1:8131/list.html
    transform:
      - jinja: "BUILD={{ env.MY_BUILD }}"
    notify:
      - python: print(content)
  - name: Jinja filters
    url: http://127.0.0.1:8131/list.html
    transform:
      - css: "section#main"
      - jinja: "TEXT={{ css(\"section#main\") | text | upper }} NUM={{ \"12.34 USD\" | float | int }}"
    notify:
      - python: print(content)
MY_BUILD=REL-42 /opt/kibitzr/venv/bin/kibitzr once
BUILD=REL-42
TEXT=HELLO
WORLD NUM=12

但凭据不在 Jinja 上下文里,引用会直接渲染失败:

checks:
  - name: Jinja creds
    url: http://127.0.0.1:8131/changed.txt
    transform:
      - jinja: "CREDS=[{{ creds.service.username }}]"
    notify:
      - python: print(content)
2026-10-27 14:53:33,847 [WARNING] kibitzr.transformer.jinja_transform: Jinja render failed
jinja2.exceptions.UndefinedError: 'creds' is undefined

⚠️ 注意:凭据(creds)只在 Python 脚本抓取/变换里可用,Jinja 模板拿不到,且渲染失败仅告警、该检查后续流水线中断。需要把凭据拼进 URL 或请求头时,用 script: python: 自行构造请求。

5.6 变换:changes

changes 通过与 pages/<name>/ 中的上一版比较产生报告,依赖系统 git。三种形态实测:

checks:
  - name: Default diff
    url: http://127.0.0.1:8131/changed.txt
    transform:
      - changes
    notify:
      - python: print("DEFAULT>>>\n" + content)
  - name: Word diff
    url: http://127.0.0.1:8131/changed.txt
    transform:
      - changes: word
    notify:
      - python: print("WORD>>>\n" + content)

把页面从 VERSION=4 改成 VERSION=5 后运行:

DEFAULT>>>
Default diff at http://127.0.0.1:8131/changed.txt
@@ -1 +1 @@
-VERSION=4
+VERSION=5
WORD>>>
[-VERSION=4-]{+VERSION=5+}
last change was 18 minutes ago

verbose 形态输出新旧值两段,适合只看最终值:

NOTIFY>>>
Build number at http://127.0.0.1:8131/changed.txt
New value:
VERSION=5
Old value:
VERSION=4

缺 git 时的报错:

cd /opt/kibitzr/work/case_changes
PATH=/opt/kibitzr/venv/bin /opt/kibitzr/venv/bin/kibitzr once
  File "/opt/kibitzr/venv/lib/python3.10/site-packages/kibitzr/storage.py", line 42, in __init__
    self.git = sh.Command('git').bake(
  File "/opt/kibitzr/venv/lib/python3.10/site-packages/sh.py", line 1310, in __init__
    raise CommandNotFound(path)
sh.CommandNotFound: git
exit=1

历史目录属主与当前运行用户不一致时,git 会拒绝操作:

fatal: detected dubious ownership in repository at '/opt/kibitzr/pages/Demo_build'
To add an exception for this directory, call:

	git config --global --add safe.directory /opt/kibitzr/pages/Demo_build

⚠️ 注意:pages/ 是 git 仓库,属主必须与运行用户一致。手工调试用 root、托管用 kibitzr 用户时,会同时踩到 PermissionError 与 dubious ownership 两类错误。统一用服务用户执行,或 chown -R kibitzr:kibitzr /opt/kibitzr/pages。

5.7 变换与通知:jq、shell、python、stash

jq 变换把 JSON 交给系统 jq 处理:

checks:
  - name: JQ
    url: http://127.0.0.1:8131/data.json
    transform:
      - jq: ".tags[]"
    notify:
      - python: print("JQ=" + repr(content))
JQ='monitor\npython'

shell 变换对内容执行外部命令;python 通知器执行任意 Python;stash 通知器把值写入键值库:

checks:
  - name: Shell transform
    url: http://127.0.0.1:8131/changed.txt
    transform:
      - shell: "tr a-z A-Z"
    notify:
      - python: print("UPPER=" + content.strip())
  - name: Shell notify
    url: http://127.0.0.1:8131/changed.txt
    notify:
      - shell: "cat > /opt/kibitzr/work/case_script/notify.out"
  - name: Stash notify
    url: http://127.0.0.1:8131/changed.txt
    notify:
      - stash:
          last_build: "{{ content | trim }}"
2026-10-27 14:51:12,789 [INFO] kibitzr.fetcher.loader: Fetching Shell transform at http://127.0.0.1:8131/changed.txt
2026-10-27 14:51:12,802 [INFO] kibitzr.notifier.custom: Executing Python notifier
2026-10-27 14:51:12,803 [INFO] kibitzr.fetcher.loader: Fetching Shell notify at http://127.0.0.1:8131/changed.txt
2026-10-27 14:51:12,812 [INFO] kibitzr.fetcher.loader: Fetching Stash notify at http://127.0.0.1:8131/changed.txt
UPPER=VERSION=4

查看 stash 实际写入的内容:

cd /opt/kibitzr/work/case_script
/opt/kibitzr/venv/bin/kibitzr stash
last_build: VERSION=4

stash 落在当前目录的 stash.db(底层文件实际名为 stash.db.db),可用于跨检查传递状态。

5.8 通知:notify 的两种写法

块式写法最清晰,也便于加多个通知器:

notify:
  - python: print(content)

单行 flow 写法同样可用(实测两个检查均正常通知):

notify: [python: print(content)]

💡 说明:单行写法在 YAML 里是「字符串列表」,Kibitzr 会把它按 键: 值 再解析一次,因此可用但可读性差。多通知器、多行 Python 时务必用块式。

5.9 creds:读取方式与「不插值」

凭据文件与 kibitzr.yml 同目录,默认权限 600。YAML 里的值按字面量读取,不做环境变量插值:

# kibitzr-creds.yml
service:
  username: ${ KZ_USER }
  password: ${ KZ_PASS }
# kibitzr.yml
checks:
  - name: Creds lookup
    script:
      python: |
        content = "USER=" + str(creds["service"]["username"]) + " PASS=" + str(creds["service"]["password"])
        ok = True
    notify:
      - python: print(content)

导出真实环境变量后运行:

KZ_USER=alice KZ_PASS=s3cret /opt/kibitzr/venv/bin/kibitzr once
2026-10-27 14:51:12,166 [INFO] kibitzr.fetcher.loader: Fetching 'Creds lookup' using script
2026-10-27 14:51:12,166 [INFO] kibitzr.fetcher.script: Fetch using Python script
2026-10-27 14:51:12,166 [INFO] kibitzr.notifier.custom: Executing Python notifier
USER=${ KZ_USER } PASS=${ KZ_PASS }

⚠️ 注意:这是实测中最容易踩的坑——文档示例里的花括号变量写法只是占位符,Kibitzr 不会替换成环境变量,而是原样当作凭据值。凭据文件里必须写明文实际值(配合 600 权限与服务用户),或在 Python 脚本中自行 os.environ 读取。凭据文件的路径固定为同目录的 kibitzr-creds.yml,不能改名。

5.10 复用与扩展:templates、batch、notifiers、pages

四个顶层扩展能力一次实测:

notifiers:
  common: |
    print("SHARED=" + content.strip())
templates:
  base:
    url: http://127.0.0.1:8131/changed.txt
    transform:
      - text
    notify:
      - python: common
checks:
  - name: From template
    template: base
  - name: Batch {}
    batch: "Batch {}"
    url-pattern: "http://127.0.0.1:8131/{}.txt"
    items:
      - changed
    transform:
      - text
    notify:
      - python: common
2026-10-27 14:51:12,957 [INFO] kibitzr.fetcher.loader: Fetching From template at http://127.0.0.1:8131/changed.txt
2026-10-27 14:51:12,977 [INFO] kibitzr.fetcher.loader: Fetching Batch changed at http://127.0.0.1:8131/changed.txt
SHARED=VERSION=4
SHARED=VERSION=4
  • templates 定义公共字段,检查用 template: 名称 引用。
  • batch 中 {} 被 items 逐项替换,生成 Batch changed;url-pattern 同步替换。
  • notifiers 定义命名通知器,python: common 直接引用。
  • pages 是 checks 的旧别名,同样可用:
pages:
  - name: Legacy pages key
    url: http://127.0.0.1:8131/changed.txt
    notify:
      - python: print("PAGES=" + content.strip())
2026-10-27 14:51:13,107 [INFO] kibitzr.fetcher.loader: Fetching Legacy pages key at http://127.0.0.1:8131/changed.txt
PAGES=VERSION=4

💡 说明:templates 与 batch 在官方配置文档中着墨不多,但 8.0.0 实测可用,适合「同站点多页面、同结构多环境」的批量监控。pages 别名主要用于兼容旧配置,新配置统一写 checks。

5.11 日志级别:-l / --log-level

可用值只有四个,非法值在参数解析阶段就被拒绝:

/opt/kibitzr/venv/bin/kibitzr -l verbose once
Usage: kibitzr [OPTIONS] COMMAND [ARGS]...
Try 'kibitzr --help' for help.

Error: Invalid value for '-l' / '--log-level': 'verbose' is not one of 'debug', 'info', 'warning', 'error'.
exit=2

默认 info;debug 会额外输出配置加载、HTTP 缓存与原始报告,适合排障;warning/error 适合生产降噪。日志默认输出到标准错误,systemd 托管后进入 journal。

5.12 状态目录:pages 与 stash.db

changes 生成的历史在 pages/<name>/(内部是 git 仓库),stash 通知器生成 stash.db.db:

/opt/kibitzr/pages
/opt/kibitzr/pages/Demo_build
/opt/kibitzr/pages/Demo_build/content
/opt/kibitzr/pages/Demo_build/.git

清理变更历史用 kibitzr clean,它删除当前目录下的 pages/:

cd /opt/kibitzr/work/case_changes2
ls pages
/opt/kibitzr/venv/bin/kibitzr clean
ls pages
Default_diff
Word_diff
exit=0
ls: cannot access 'pages': No such file or directory

六、生产配置模板

6.1 周期轮询型(适合版本号、库存、价格)

适用场景:2 核 4GB 以上、单机、检查项 20 个以内、允许 5 分钟级延迟。

notifiers:
  log: |
    print("[{name}] {content}".format(name=conf["name"], content=content.strip()))

templates:
  web-text:
    transform:
      - text
    notify:
      - python: log
    period: 5 minutes

checks:
  - name: pay-api-version
    template: web-text
    url: https://pay.example.com/health

  - name: order-page-build
    template: web-text
    url: https://order.example.com/version.txt
    transform:
      - text
      - changes: verbose

逐项说明:templates 复用抓取与通知,period: 5 minutes 统一节奏;使用 changes 的检查会自动得到 git 历史,clean 前注意备份 pages/。

6.2 日历调度型(适合日报、活动页)

适用场景:检查项少、只关心固定时间点的变化。

checks:
  - name: daily-report-page
    url: https://ops.example.com/daily
    transform:
      - css: "#summary"
      - text
      - changes
    schedule:
      every: day
      at: "08:30"
    notify:
      - python: |
          print(content)

  - name: weekly-activity
    url: https://ops.example.com/activity
    transform:
      - text
      - changes: word
    schedule:
      every: monday
      at: "09:00"
    notify:
      - python: |
          print(content)

调度会先立即执行一轮再进入循环,因此部署当天会立刻产生一次基线报告,这是正常现象。

七、常见问题

7.1 启动报 kibitzr.yml not found

Q1: 明明有配置文件,却提示找不到。

A: 只按 ./kibitzr.yml → ~/.config/kibitzr/kibitzr.yml → ~/kibitzr.yml 查找,且不支持 --config。用 -l debug 看 Loading settings from 的实际路径,或 cd 到配置目录再执行。

7.2 ConfigurationError: No fetcher is applicable

Q2: 检查项没写 url 也没写 script。

A: 每个检查必须至少有一个数据源。补充 url 或 script 后重试:

kibitzr.exceptions.ConfigurationError: No fetcher is applicable for "{conf["name"]}"

报错信息不会显示具体检查名,建议按配置顺序逐个注释排查。

7.3 changes 报 sh.CommandNotFound: git

Q3: 使用 changes 变换立即失败。

A: 安装系统 git:

apt-get install -y git
git --version
git version 2.34.1

7.4 fatal: detected dubious ownership

Q4: 手工用 root 跑过,切到 kibitzr 用户后 changes 失败。

A: pages/ 是 git 仓库,属主不一致会触发 git 的安全检查。统一属主:

chown -R kibitzr:kibitzr /opt/kibitzr/pages

不要用 git config --global --add safe.directory 绕过,这会把所有同名目录都放行,削弱安全检查。

7.5 creds 里的环境变量没有被替换

Q5: 凭据文件里写了花括号包裹的环境变量名,脚本拿到的是字面量。

A: Kibitzr 不做环境变量插值。直接在 kibitzr-creds.yml 写实际值并保持 600 权限,或在 Python 脚本里用 os.environ["KZ_USER"] 读取。

7.6 Jinja 报 'creds' is undefined

Q6: 想在 Jinja 模板里引用凭据。

A: Jinja 上下文只有 env、css() 与内容本身,没有 creds。把需要凭据的请求放到 script: python: 里完成。

7.7 浏览器检查卡住或报 NoSuchDriverException

Q7: 配置 delay 后长时间无输出,最终超时。

A: delay 需要 Firefox 与 geckodriver,首次运行由 selenium-manager 获取驱动。纯 HTTP 场景删掉 delay;确有需要时预装浏览器并保证驱动获取链路可用。

7.8 kibitzr reload 把命令自己杀掉了

Q8: 执行 reload 后当前命令异常退出,退出码 138。

A: 重载指令本身已生效,138 是命令自身收到 SIGUSR1(User defined signal 1)被终止的结果。脚本中改用:

kill -USR1 "$(systemctl show -p MainPID --value kibitzr)"

该命令退出码 0,服务端日志同样出现 Received SIGUSR1. Flagging configuration reload。不需要热重载时直接 systemctl restart kibitzr。

7.9 通知重复发送

Q9: 同一个检查在固定时间前后连续收到两次通知。

A: 同时配置了 period 与 schedule,两套调度都在生效。只保留一种,并确认 once 不是常驻调度。

八、参考链接

  • 官方配置文档:https://kibitzr.readthedocs.io/en/latest/configuration.html
  • 官方变换文档:https://kibitzr.readthedocs.io/en/latest/transforms.html
  • 官方调度文档:https://kibitzr.readthedocs.io/en/latest/schedule.html
  • 官方凭据文档:https://kibitzr.readthedocs.io/en/latest/credentials.html
  • 官方仓库:https://github.com/kibitzr/kibitzr
  • 本专栏安装篇:《Kibitzr 8.0.0 安装与首个监控任务》
  • 本专栏认知篇:《Kibitzr 8.0.0 完整部署与实战指南》