名称: 日志检查 描述: 检查和查询钩子事件日志。子命令: sessions, timeline, trace, agents, team, stats, coverage, daily. 用户可调用: true 参数提示: “<子命令> [选项]” 允许工具: Read, Bash, Glob, Grep
日志检查
从 JSONL 文件中查询和检查可观测性钩子日志。
参数
子命令是第一个参数:
sessions– 列出所有会话,包括事件计数和持续时间timeline [session-id]– 按时间顺序显示事件列表,带有序列号trace <tool-use-id>– 显示工具调用的前后配对agents– 子代理生命周期(启动/停止/持续时间)team– 队友闲置和任务完成活动stats– 聚合统计(事件分解、工具使用、损坏计数)coverage– 观察到的 14 种事件类型daily– 从 sessions-index.jsonl 显示每日摘要(v1.6.0)
日志文件位置
在以下位置查找日志文件:
{项目}/.claude/logs/hooks/events-{YYYY-MM-DD}.jsonl
{项目}/.claude/logs/hooks/events-{YYYY-MM-DD}.jsonl.gz (压缩的冷日志)
使用 Glob 查找所有可用的日志文件:
Glob(".claude/logs/hooks/events-*.jsonl")
Glob(".claude/logs/hooks/events-*.jsonl.gz")
注意: 超过 7 天的文件会自动压缩为 .jsonl.gz(可通过 CLAUDE_HOOK_LOG_COMPRESS_AFTER_DAYS 配置)。DuckDB 原生读取 .jsonl.gz;在 Unix 上使用 zcat | jq 进行手动检查。
工作流程
步骤 1: 定位日志文件
在项目中查找所有钩子日志文件:
Glob(".claude/logs/hooks/events-*.jsonl")
Glob(".claude/logs/hooks/events-*.jsonl.gz")
Glob(".claude/logs/hooks/sessions-index.jsonl")
如果日志文件不存在,通知用户必须通过 CLAUDE_HOOK_LOG_EVENTS_ENABLED=1 启用日志记录。
步骤 2: 执行子命令
sessions
如果存在,解析 sessions-index.jsonl。使用 type 字段区分条目(session_start, session_end, agent_start, agent_stop)。为了向后兼容,没有 type 的条目根据 started_at(session_start)或 ended_at(session_end)的存在进行推断。否则,扫描事件日志文件中的 sessionstart 和 sessionend 事件。显示:
| 会话 ID | 开始时间 | 结束时间 | 模型 | 事件数 | 持续时间 |
|---|
增强的索引还包括 cwd, transcript_path, model, 和 agent_type 在 session_start 条目上。
timeline [session-id]
读取日志文件并按序列顺序显示事件。如果提供 session-id,则过滤到该会话。显示:
序列号 时间戳 事件 工具名称 进程ID
1 2026-02-15T18:13:23 sessionstart - 12345
2 2026-02-15T18:13:24 userpromptsubmit - 12345
3 2026-02-15T18:13:25 pretooluse Read 12346
4 2026-02-15T18:13:25 posttooluse Read 12346
trace <tool-use-id>
找到具有给定 tool_use_id 的 pretooluse 和 posttooluse/posttoolusefailure 条目。显示配对,展示时间和成功/失败。
agents
首先检查 sessions-index.jsonl 中的 agent_start 和 agent_stop 条目(直接查找,无需事件扫描)。如果索引缺失,则回退到扫描事件日志文件中的 subagentstart 和 subagentstop 事件。显示:
| 代理 ID | 类型 | 开始时间(序列) | 停止时间(序列) | 持续时间 |
|---|
team
首先检查 sessions-index.jsonl 中的 task_completed 和 teammate_idle 条目(v1.8.0 添加的直接查找)。如果索引条目缺失,则回退到扫描事件日志文件中的 teammateidle 和 taskcompleted 事件。显示:
| 时间 | 类型 | 队友 | 团队 | 任务主题 |
|---|
stats
解析所有条目并计算:
- 事件分解: 每种事件类型的计数
- 工具使用: 最频繁使用的工具(从 pretooluse 事件)
- 错误率: posttoolusefailure 计数 / 总工具使用次数
- 平均持续时间_ms: 平均钩子处理时间
- 损坏计数: JSON 解析失败的行数
- 唯一会话数: 不同 session_id 值的计数
- 序列范围: 最小和最大序列值
使用带有 Python 单行命令的 Bash 进行聚合。对于 .jsonl.gz 文件,使用 gzip.open 读取:
python3 -c "
import json, gzip, glob
from collections import Counter
events = Counter()
tools = Counter()
errors = 0
corrupt = 0
durations = []
sessions = set()
def read_lines(path):
if path.endswith('.gz'):
with gzip.open(path, 'rt', encoding='utf-8') as f:
return f.readlines()
with open(path, 'r', encoding='utf-8') as f:
return f.readlines()
for path in sorted(glob.glob('.claude/logs/hooks/events-*.jsonl') + glob.glob('.claude/logs/hooks/events-*.jsonl.gz')):
for line in read_lines(path):
line = line.strip()
if not line:
continue
try:
e = json.loads(line)
events[e.get('event','')] += 1
if 'tool_name' in e:
tools[e['tool_name']] += 1
if e.get('event') == 'posttoolusefailure':
errors += 1
durations.append(e.get('duration_ms', 0))
sessions.add(e.get('session_id',''))
except json.JSONDecodeError:
corrupt += 1
print(f'事件: {dict(events)}')
print(f'顶部工具: {tools.most_common(10)}')
print(f'错误率: {errors}/{events.get(\"pretooluse\",0)} 工具使用次数')
print(f'平均持续时间: {sum(durations)/len(durations):.2f}ms' if durations else '无条目')
print(f'损坏行数: {corrupt}')
print(f'唯一会话数: {len(sessions)}')
"
注意: hook_event_name 在 v1.4.0 中从条目中移除,并在 v1.7.0 中从 extra_fields 中完全抑制。
coverage
检查已捕获的 14 种事件类型。显示:
事件覆盖率报告
=====================
[x] pretooluse (142 事件)
[x] posttooluse (140 事件)
[x] userpromptsubmit (8 事件)
[ ] precompact (0 事件)
...
覆盖率: 12/14 事件类型已观察
14 种必需类型是: pretooluse, posttooluse, posttoolusefailure, permissionrequest, notification, userpromptsubmit, stop, subagentstart, subagentstop, precompact, sessionstart, sessionend, teammateidle, taskcompleted.
daily
解析 sessions-index.jsonl 中的 daily_summary 条目(v1.6.0)。显示:
| 日期 | 事件数 | 会话数 | 错误数 | 平均持续时间 |
|---|
每日摘要每天在第一个 sessionstart 时自动生成,基于前一天的日志。
步骤 3: 格式化输出
以清晰、可读的格式呈现结果。适当时使用 Markdown 表格。在标题中包含日志文件路径和日期范围。
DuckDB 查询(高级)
对于大型日志文件,建议使用 DuckDB 进行分析查询:
-- 直接加载 JSONL
SELECT event, count(*) as cnt
FROM read_json_auto('.claude/logs/hooks/events-2026-02-15.jsonl')
GROUP BY event ORDER BY cnt DESC;
-- 工具使用时序
SELECT tool_name, count(*) as uses, avg(duration_ms) as avg_ms
FROM read_json_auto('.claude/logs/hooks/events-2026-02-15.jsonl')
WHERE event = 'pretooluse'
GROUP BY tool_name ORDER BY uses DESC;
-- 查找损坏条目(序列间隙)
SELECT seq, lag(seq) OVER (ORDER BY seq) as prev_seq
FROM read_json_auto('.claude/logs/hooks/events-2026-02-15.jsonl')
WHERE seq - lag(seq) OVER (ORDER BY seq) > 1;
使用示例
# 列出会话
/claude-code-observability:log-inspection sessions
# 当前会话的时间线
/claude-code-observability:log-inspection timeline
# 跟踪特定工具调用
/claude-code-observability:log-inspection trace toolu_01abc123
# 事件类型覆盖率
/claude-code-observability:log-inspection coverage
# 完整统计
/claude-code-observability:log-inspection stats