依赖扩展与观测

July 29, 2026 · View on GitHub

可选 extras

extra用途
htmlkit实验性 HTMLKit/litehtml Provider
playwrightPlaywright Provider
takumiTakumi Provider
pillow独立 Pillow RasterScene Capability
skia独立 Skia RasterScene Capability
filehost为内置 HTTP asset publisher 增加 py-machineid 守卫标识来源
sentrySentry spans 与 metrics
prometheusPrometheus metrics
all安装上述全部能力,包括具有平台限制的 Skia
uv add "nonebot-plugin-htmlrender[playwright,sentry,prometheus]>=0.8.0,<0.9"

HTMLKit/Playwright/Takumi 的引擎库缺失会形成可诊断的 Provider availability;插件不会在 import 时无条件加载所有引擎。选择 HTMLKit 时,bootstrap 会在 NoneBot startup 前只加载其对应插件,以注册上游 Fontconfig 初始化 hook。filehost transport由 htmlrender 自有的 HostedAssetStore 提供,bootstrap 会在 FastAPI ASGI host启动前安装固定路由和请求头守卫,不再加载第二个 NoneBot filehost 插件。filehost extra 只提供 py-machineid 作为默认守卫值的机器标识来源;未安装时会使用内置回退,也可以显式配置 request_header_value

Pillow/Skia 只在 render.graphics.backends 显式配置后加载,并形成独立 typed Capability,不进入 Provider discovery。Skia 没有 sdist/musllinux wheel,并要求manylinux_2_28、macOS 11+ 或受支持的 Windows wheel;Linux 还必须提供libEGL.so.1libGL.so.1libexpat.so.1 等运行库。Alpine/musl 或旧 glibc 镜像不要安装 skiaall extra;完整平台矩阵与安装命令见 Skia 后端

HTMLKit 当前精确锁定 nonebot-plugin-htmlkit==0.1.0rc5。它不引入 Playwright 或Pillow,但属于 prerelease,平台 wheel、选项限制和 Fontconfig 生命周期见HTMLKit 配置

开启观测

render:
  provider: playwright
  observability:
    sentry: true
    prometheus: true

完整路径为 render.observability.sentryrender.observability.prometheus,默认均为 false。开关启用后 bootstrap 会自动尝试 require 对应 NoneBot 集成插件;未安装或加载失败只记录 warning,不会让渲染运行时启动失败。

htmlrender 只负责产生 span 与指标,不负责重复配置 exporter:Sentry 的 DSN、采样、transport 和 release 等设置仍由 nonebot_plugin_sentry/Sentry SDK 管理;Prometheus registry 与 HTTP endpoint 仍由 nonebot_plugin_prometheus 管理。两个 NoneBot 插件会在 htmlrender 导入阶段按开关提前加载,使它们来得及注册 startup hook;不要等到第一次渲染后才动态加载集成。

插装边界

composition 为每个 Application 创建 observer,并注入 Provider、Graphics backend、Resource Service、template compiler 与 Capability。Sentry 和 Prometheus 同时开启时,同一个 observer 将一次操作扇出到两个 exporter,不引入第二套渲染生命周期或参数契约;两者都关闭时 composition 直接注入 no-op observer。

调用面观测范围operation
通用渲染Provider executor 的完整调用playwright.html_render.rasterize_htmltakumi.rasterize_htmlhtmlkit.rasterize_html
Provider 生命周期runtime acquisition、启动与关闭acquisition 使用内部兼容名称;启动与关闭为 render.startuprender.shutdown
Provider runtime引擎创建、连接与释放子步骤playwright.open_runtimeplaywright.open_sessiontakumi.open_runtimetakumi.close_runtime
Graphicsdraw 与 encode 的完整调用graphics.pillow.render_scenegraphics.skia.render_scene
Playwright 原生访问async with app.extensions.playwright.page().browser() 的完整租约playwright.native.pageplaywright.native.browser
Takumi 托管 APIapp.extensions.takumi.api() 返回对象的每个异步方法takumi.api.*
Takumi 原生访问async with app.extensions.takumi.renderer() 的完整租约takumi.native.renderer

Playwright 的 Page.goto()Locator.screenshot() 等调用位于playwright.native.page span 内,但 htmlrender 不代理 Playwright 对象,也不会为每个上游方法再建立一层 operation。若 Playwright 自身另有 tracing,它可以在同一调用中独立工作。Takumi 的托管 API 可以在不牺牲上游类型的情况下按方法插装;直接取得原生Renderer 时则与 Playwright 相同,只观测整个租约,不猜测或拦截其内部调用。原生上下文内未被调用方捕获的上游异常会把该上下文标记为失败,但异常类型本身保持不变;调用方在上下文内部捕获并吞掉的异常不会被 observer 推断为失败。

生成的 operation span 总会包含 render.backend,结束时补充 render.statusokerror)与 render.duration_seconds。具体调用还可能提供稳定的低基数属性,例如render.formatrender.accessrender.cache_hit

Sentry 插装

存在当前 Sentry span 时,htmlrender 创建 child span;否则创建以 operation 为 op和 name 的 root transaction,并把 transaction source 设为 task。是否实际采样、如何上传完全遵循 Sentry SDK 配置。Sentry SDK 没有可用 metrics surface 时,trace仍可工作,指标则安全跳过。

生成的失败 span 总会附加 error.type。若异常属于 RenderingError,还会附加error.messageerror.message_truncatederror.cause_typeserror.causes_truncated;这些值来自有界错误快照,不包含 native 异常对象。这里仅标记 span,不额外把同一个异常捕获为 Sentry event。

Prometheus 插装

Prometheus collector 在首次使用时按进程惰性创建并复用,避免重复注册。操作 counter与 duration histogram 使用相同的 opbackendstatus 标签。当 Sentry span提供 trace ID 时,htmlrender 会尽力把 trace_id exemplar 同时写入 counter 与histogram;客户端版本或存储后端不支持 exemplar 时自动回退为普通观测。

/metrics 路由、registry 选择和抓取配置属于 nonebot_plugin_prometheus,不是htmlrender 的公共接口。启用 htmlrender 的 Prometheus 开关只表示向该集成注册并更新collector。

稳定指标 schema

含义Prometheus类型与 labelsSentry类型与 tags
操作次数nonebot_htmlrender_operations_totalcounter;opbackendstatusnonebot.htmlrender.countcount;opbackendstatus
操作耗时nonebot_htmlrender_duration_secondshistogram;opbackendstatusnonebot.htmlrender.durationdistribution(second);opbackendstatus
缓存事件nonebot_htmlrender_cache_eventscounter;cacheeventnonebot.htmlrender.cache.eventscount;cacheevent
缓存条目nonebot_htmlrender_cache_entriesgauge;cachenonebot.htmlrender.cache.entriesgauge;cache
缓存驻留字节nonebot_htmlrender_cache_resident_bytesgauge;cachenonebot.htmlrender.cache.resident_bytesgauge(byte);cache

当前 cache 值由 composition 固定为 resourcetemplate_environmentfilehosttakumi_compiledevent 使用 hitmissloadwaiteviction 中适用于该缓存的子集。仅 byte-weighted cache 会更新 resident_bytes

指标表示的具体缓存层并不相同:template_environment entries 是 Environment 数量,不包含内层 compiled templates;filehost entries 是 publisher mapping;takumi_compiled 的 resident bytes 是输入 source weight,不是 native heap 精确值。按现象选择指标和调优动作见缓存组件、失效与调优

操作指标只使用稳定的 operation、provider identity 与 status 维度。当前导出schema 中 provider identity 的 label 名保留为 backend;它是兼容性字段,不是公共架构概念。路径、URL、HTML、模板变量、字体名、digest、资源内容、错误消息和 cause type 都不会进入指标标签。

常用 PromQL 示例:

sum by (op, backend, status) (
  rate(nonebot_htmlrender_operations_total[5m])
)
histogram_quantile(
  0.95,
  sum by (le, op, backend) (
    rate(nonebot_htmlrender_duration_seconds_bucket[5m])
  )
)

多进程部署会由每个 worker 持有自己的 registry、cache gauge 与运行时;聚合规则应以 Prometheus 抓取到的实例标签区分进程,不能把单进程 cache gauge 当作集群总量。

故障隔离

observer 由 composition 注入。Sentry/Prometheus 写入失败只降低观测质量,不会替换成功的渲染结果,也不会覆盖原始业务异常。自定义 Provider 不应自行创建 exporter;使用 ProviderDependencies 提供的 operation/cache observer,并为operation 与属性选择固定、低基数的值。

bootstrap 发现已启用的集成缺失或加载失败时会记录 warning;SDK API 不兼容、collector 注册或写入失败会记录 htmlrender.telemetry warning。无可用 Sentry trace时还会输出 operation、backend、status 与 duration 的 debug 日志作为本地诊断回退。可选插件的首次加载结果会在进程内缓存;修复安装或启动配置后需要重启进程。