插件业务事件日志

August 11, 2026 · View on GitHub

插件自己的业务日志是排障事实,不受 NoneBot 插件加载或 matcher 生命周期降噪影响。生产 INFO 会隐藏的是框架的 Succeeded to load pluginhandledcompletecancelledignored 等高频行;插件应显式记录自己的准备、执行和结果。

内置、官方和站点私有插件统一从 pallas.api.logging 导入 format_plugin_eventregister_plugin_startup_ready。不要导入 pallas.core,也不要用全局 matcher 包裹自动推断生命周期。

事件

事件何时记录级别必填字段
Ready路由、依赖和后台任务已可用时注册;随启动摘要输出,每插件每次启动最多一条INFOplugin、commands
操作开始显式命令真正开始执行时,例如 DrawGenerateSoberUpINFO业务主体、bot、group、user
操作完成实际副作用成功后,例如消息已经发送INFO领域结果、耗时
操作失败命令抛出异常时ERROR业务主体、异常类型
异步操作队列、跨进程或延后投递的 QueueGenerateDeliver 等状态变化INFO / ERRORtask、必要的关联 ID
Reply / Fanout / Reaction无法由具体领域操作表达的实际外发结果INFO / ERROR目标与结果字段
Skipped权限、冷却、缓存、去重、背压等未执行原因DEBUG原因

操作标签是插件内部稳定实现名的展示形式,统一转为 PascalCasearcana.drawDrawsober_upSoberUpclear_sessionClearSession。模块列负责显示插件归属,因此标签不重复插件名。

ReplyFanoutReaction 只用于没有更精确领域动词的外发结果。Draw 这类完成日志已经表达了实际回复的业务含义,不再额外输出重复的 Reply。不为每个 matcher 输出准备、执行、结束日志,避免重新引入框架噪声。

格式

标签和标识符使用方括号,正文必须是完整的业务句子,优先写明动作产生的领域结果,避免 outcome [sent]request [...] 等流水账式字段罗列。request 只在跨进程、回调或需要关联上下游时出现,优先使用原始消息 ID 或上游请求 ID;task 使用队列或任务系统中的稳定 ID,不能为日志临时生成 UUID。

{Arcana}  [Ready] Plugin [arcana] registered commands [Draw].
{Arcana}  [Draw] Bot [100000001] began a tarot draw for user [100000003] in group [100000002].
{Arcana}  [Draw] Bot [100000001] drew [The Fool upright] for user [100000003] in group [100000002] in [18ms].
{Arcana}  [Draw] Bot [100000001] failed to draw for user [100000003] in group [100000002]: FileNotFoundError.
{LLMChat} [Generate] Task [chat-01J...] queued to generate a reply in group [100000002] for request [100000004].
{Repeater}[Fanout] Bot [100000001] sent a fanout reply in group [100000002]: ...
{Repeater}[Reaction] Bot [100000001] reacted to message [100000004] in group [100000002] with emoji [66].

接入方式

在模块加载期登记就绪状态;命令 handler 在开始时记录领域动作,在真实副作用成功后记录领域结果。异常必须保留堆栈,不吞掉异常。

from nonebot import logger

from pallas.api.logging import format_plugin_event, register_plugin_startup_ready

register_plugin_startup_ready("arcana", ["arcana.draw"])

logger.info(
    format_plugin_event(
        "draw",
        f"Bot [{event.self_id}] began a tarot draw for user [{event.user_id}] "
        f"in group [{event.group_id}]",
    )
)

try:
    await bot.send(event, message)
except Exception as exc:
    logger.exception(
        format_plugin_event(
            "draw",
            f"Bot [{event.self_id}] failed to draw for user [{event.user_id}] "
            f"in group [{event.group_id}]: {type(exc).__name__}",
        )
    )
    raise

format_plugin_event 负责 PascalCase 标签和单行格式;调用方负责选择准确的业务边界、领域叙事和日志级别。首个试点是站点插件 Arcana,后续按命令入口、异步任务和外发路径分批覆盖 core 与 bundled play 插件。