监听、Hook 与后台任务
从事件级 Hook、进程级后台任务到组织级 OTEL 遥测,串起 Claude Code 三层监听体系。
Claude Code 里说到 "监听" 其实覆盖了三层完全不同的东西:一层是会话内部的事件监听,一层是长跑进程的完成通知,一层是全组织的用量遥测。这三层的目标和实现机制差别很大,混着谈会一直讲不明白。理清楚这三层之后,很多需求就自然对号入座了。
三层监听的边界
- 事件级:Hook。会话内部的生命周期钩子,触发点覆盖 SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、Notification、Stop、PreCompact 等一整套 lifecycle 事件。它跑在 Claude 主循环的关键路径上,可以直接决定某个动作是不是继续。
- 进程级:Bash 工具的
run_in_background加上 task-notification。开一个长跑任务,主循环继续做别的事,任务完成时会作为用户角色消息自动回调到 agent。适合跑测试、构建、批量下载这类耗时活。 - 组织级:OpenTelemetry 遥测配合 Analytics API。给团队 CTO、SRE 或者财务看的用量、成本、活跃用户视角。数据周期性推到自建 collector 或者官方看板,不参与实时决策。
三层的时间尺度不同,Hook 在毫秒级动作,后台任务在秒到分钟级,遥测在小时到月度级。设计监控策略时,先想清楚要在哪一层解决,别用 Hook 硬撑长任务,也别指望 OTEL 拦截危险命令。
Hook 简明复习
一个 Hook 定义由三部分组成:event 名字、matcher(决定什么工具或场景触发)、command(要跑的脚本)。脚本从 stdin 拿到一段 JSON,里面带 session_id、cwd、tool_name、tool_input 等信息,返回通过退出码和 stderr 告知 Claude。
- 退出码 0 表示放行,PreToolUse 里意味着交回给正常的权限流程判定。
- 退出码 2 表示阻断,stderr 的文字会作为反馈发回给 Claude 让它自己调整。
- 更复杂的场景就用 stdout 输出结构化 JSON,控制
permissionDecision、additionalContext、updatedInput等字段。
三个典型的实时监听 Hook
PreToolUse 拦截危险命令并弹通知:所有 Bash 调用先走一遍脚本,命中黑名单就 exit 2 阻断,同时调用系统通知让用户手动确认。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-bash.sh"
}
]
}
]
}
}PostToolUse 推审计日志:每次工具执行完把 tool_name、耗时、成功与否写到外部日志,方便安全团队回查。
#!/bin/bash
INPUT=$(cat)
echo "$INPUT" | jq -c '{ts: now, tool: .tool_name, cwd: .cwd}' \
>> ~/.claude/audit.log
exit 0Stop 事件汇总本次会话开销:Claude 一个 turn 结束时触发,读取 transcript 算 token 用量与花销,通过 curl 推到 Slack Webhook。这个 Hook 的关键是避免阻塞主循环,脚本内部要短平快,重活可以在脚本里再 fork 一个后台进程去做。
后台任务:Bash 的 run_in_background
对于跑测试、构建镜像、批量转码这种需要几分钟才有结果的任务,同步等就是浪费上下文。Bash 工具支持 run_in_background: true,命令启动后立刻返回一个进程句柄,主循环可以继续处理别的事。
任务完成或者产出新一行 stdout 时,会以 task-notification 的形式作为用户角色消息注入到会话里,agent 就能在下一个 turn 收到并做反应。这个机制不需要显式轮询,也不会把中间日志一次性灌进上下文。配合 Monitor 工具还能对已经启动的进程做 stream 式追踪,每一行输出都会作为通知回来,非常适合 tail 服务日志或者跟踪训练进度。
在 headless 场景里,claude -p 结束前会等所有后台任务收尾,超过 CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS 就直接把子进程留给操作系统。这一点在 CI 里尤其要留心,避免脚本挂着不退出。
后台任务和 Hook 的分工
Hook 是同步的、跑在事件路径上的过滤器,超时会阻塞 Claude。真正耗时的工作要放到 Bash 后台任务或者用 type: "http" 的 Hook 把重活扔给外部服务。
组织级:OpenTelemetry 遥测
想要给整个团队做用量看板,就打开 Claude Code 自带的 OTEL 支持。核心几个环境变量:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"指向自己的 OTLP collector 之后,指标和事件会周期性推过来。可以看到的关键指标包括 claude_code.session.count、claude_code.token.usage、claude_code.cost.usage、claude_code.lines_of_code.count、claude_code.active_time.total。事件流里还有 user_prompt、tool_result、api_request 这些细颗粒记录。
多团队场景里用 OTEL_RESOURCE_ATTRIBUTES 打业务维度标签:
export OTEL_RESOURCE_ATTRIBUTES="department=engineering,team.id=platform,cost_center=eng-123"这样在 Grafana 里就能按部门、按 cost center 切片,做费用摊分或者对比不同团队的 Claude Code 使用密度。
Analytics API 与团队看板
比 OTEL 更轻量的路径是 Team 或 Enterprise 计划自带的 Analytics 面板。管理员在控制台就能看到日活、Session 数、代码接受率、按用户排行的贡献榜,还有 spend、lines this month 这类财务视角。装上 Claude Code 官方 GitHub App 之后还能自动把 PR 和 Claude Code 会话做归因,算出 "PRs with CC" 占比、每人写了多少 AI 辅助的行数。归因算法会排除 lock 文件、构建产物、生成代码,只算真正有效行,并且开发者后期改动超过 20% 的部分不会算在 Claude 头上。
程序化拉取用 read:analytics scope 的 API key,可以导出 CSV 或者对接自家 BI 系统。API 客户没有 Team 面板的话,官方也提供了单独的 Members 视图,按 API key 或者邮箱查月度花费和接受行数。
常见坑
- Hook 不要写慢逻辑。command 类默认十分钟超时,但 UserPromptSubmit 只有 30 秒、MessageDisplay 只有 10 秒。做耗时事的时候在脚本里 nohup 一个后台进程再退出。
- 遥测默认对 prompt 和响应做脱敏,不到必要不要打开
OTEL_LOG_USER_PROMPTS=1或OTEL_LOG_RAW_API_BODIES,否则可能把用户输入的密钥或客户数据流到 collector 里。 - 多个 Hook 并发运行,如果都改
updatedInput,最后完成的那个胜出。有相同事件多个改写就写单个入口再内部合并。 run_in_background启动的进程会随会话生命周期挂着,别用它做长驻服务,那种要放到独立 systemd 或 nssm。
参考来源
- https://docs.claude.com/en/docs/claude-code/monitoring-usage:OpenTelemetry 遥测配置、指标、事件、多团队属性的完整参考
- https://docs.claude.com/en/docs/claude-code/analytics:Team 与 Enterprise 版 Analytics 面板,包含 PR 归因规则
- https://docs.claude.com/en/docs/claude-code/hooks:Hook 事件列表、matcher 语法与 JSON 输出协议的官方参考
- https://docs.claude.com/en/docs/claude-code/hooks-guide:Hook 入门指南,含通知、格式化、文件保护等常见配方