关于 debug-local
面向 Lightdash 开发者的本地调试指南,要求先基于日志、追踪和复现确认根因,再修复并验证。指南汇集 PM2、Maple、浏览器和数据库的调查方法。
主要功能
- 01先收集证据并确认根因
- 02通过 Maple 查询追踪与错误
- 03按常见模式定位故障
- 04验证假设后编写回归测试
- 05检查修复并生成调试报告
适用场景
- 01调查 API 错误或响应缓慢
- 02排查权限、缓存或迁移问题
- 03定位前端显示和调度器故障
- 04确认修复有效并记录证据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lightdash/ lightdash --skill debug-local安装后在 Claude Code 中输入/debug-local确认已加载。
详细介绍
调试 Lightdash
铁律
未先调查根本原因,不得修复。
在获得证据(日志、跟踪记录、复现结果)支持的明确根因假设之前,不要编辑应用代码。修复表面症状会导致反复调试。
阶段 1:收集证据
形成任何假设之前,先收集事实。
1. 收集症状
阅读错误消息、堆栈跟踪和复现步骤。如果用户提供的上下文不足,只问一个澄清问题。
2. 检查近期变更
git log --oneline -20 -- <affected-files>
之前能正常工作吗?如果是回归问题,根本原因就在差异中。
3. 检查日志和跟踪记录
pnpm exec pm2 logs lightdash-api --lines 50 --nostream
然后查询 Maple(本地模式会从 API 和 scheduler 接收 OTLP 跟踪记录):
maple services --format table # per-service throughput and error rate
maple errors --since 5m # errors grouped by fingerprint
maple traces --since 5m --errors # recent traces containing errors
maple trace <trace-id> # full span tree + correlated logs
maple slow-traces --since 15m # slowest traces
CLI 默认使用端口 4318,因此请先将其指向此实例:
export MAPLE_LOCAL_URL="http://127.0.0.1:${MAPLE_PORT:-4320}"。完整的筛选条件请参阅
跟踪记录查询。
4. 复现
能否稳定触发此错误?使用浏览器自动化或 curl 复现。如果无法复现,请先收集更多证据再继续。
5. 阅读代码
从症状出发,沿代码路径追溯可能的原因。使用 Grep 查找所有引用,使用 Read 理解逻辑。
**输出:**陈述你的根因假设——明确、可测试地说明哪里出了问题,以及原因是什么。
阶段 2:模式匹配
检查错误是否符合已知的 Lightdash 模式:
| 模式 | 特征 | 检查位置 |
|---|---|---|
| 权限错误 | 403 Forbidden | projectMemberAbility.ts、organizationMemberAbility.ts 中的 CASL 权限 |
| 查询缓慢 / N+1 | 响应时间 >1s,数据库跟踪片段较多 | maple trace <id> 跟踪片段树——统计 db.* 跟踪片段,检查是否存在循环 |
| Explore 缓存过期 | 显示旧列/指标 | cached_explores 表、CompileService |
| 迁移问题 | 找不到列、架构不匹配 | packages/backend/src/database/migrations/,检查回滚 |
| 调度器任务失败 | 任务未运行或卡住 | PM2 scheduler 日志、graphile_worker.jobs 表 |
| 前端数据过期 | 变更后 UI 仍显示旧数据 | TanStack Query 缓存失效,检查 queryClient.invalidateQueries |
| 竞态条件 | 间歇性出现,依赖时序 | 对共享状态的并发访问、缺少 await |
| 跨服务文件问题 | 在 worker/headless 中找不到文件 | 必须通过 FileStorageClient 使用 S3,而非本地文件系统 |
| 配置漂移 | 本地运行正常,但在 CI/staging 中失败 | 环境变量、.env.development.local 与容器环境的差异 |
| 空值传播 | TypeError、无法读取属性 | 可选值缺少保护、Knex .first() 返回 undefined |
另请检查:
- 使用
git log查看同一区域之前的修复——同一文件反复出现的问题是架构层面的隐患 - 确认问题出在 common、backend 还是 frontend——错误归因到其他层会浪费时间
阶段 3:检验假设
编写任何修复代码之前,先验证你的假设。
-
**确认:**添加临时日志、断言,或使用 Maple/PM2 检查怀疑的根因。然后复现。证据是否吻合?
-
**如果假设错误:**返回阶段 1。收集更多证据。不要猜测。
-
**三次失败规则:**如果 3 个假设都不成立,立即停止。询问用户:
- 使用新的假设继续(请描述该假设)
- 升级问题,交由人工审查
- 添加检测手段,等待下次捕获问题
阶段 4:修复
确认根本原因后:
-
**修复根本原因,而非表面症状。**以最小改动消除实际问题。
-
**最小化差异。**尽可能少改文件、少改代码行。不要重构相邻代码。
-
编写回归测试,确保没有修复时测试失败,修复后测试通过。
-
**运行相关测试套件。**粘贴输出。
-
**影响范围检查:**如果修复涉及的文件超过 5 个,请暂停并先向用户确认。
阶段 5:验证并报告
复现原始问题场景,并确认问题已修复。 这一步不可省略。
输出结构化的调试报告:
DEBUG REPORT
════════════════════════════════════════
Symptom: [what the user observed]
Root cause: [what was actually wrong]
Fix: [what was changed, with file:line references]
Evidence: [test output, trace showing fix works]
Regression test: [file:line of the new test]
Status: DONE | DONE_WITH_CONCERNS | BLOCKED
════════════════════════════════════════
状态定义:
- DONE — 已找到根本原因、应用修复、编写回归测试,且测试通过
- DONE_WITH_CONCERNS — 问题已修复,但无法完全验证(例如,间歇性问题或需要在预发布环境中验证)
- BLOCKED — 调查后仍不清楚根本原因,已上报给用户
前置条件
确保开发环境正在运行:
/docker-dev # Start Docker services (postgres, minio, etc.)
pnpm pm2:start # Start all PM2 processes including Maple
pnpm pm2:status # Verify all processes are online
服务:
- 前端:http://localhost:3000
- API:http://localhost:8080
- Maple trace UI:http://localhost:4320
工具参考
服务端日志(PM2)
pnpm pm2:logs # Stream all logs (Ctrl+C to exit)
pnpm pm2:logs:api # API server logs only
pnpm pm2:logs:scheduler # Background job scheduler logs
pnpm pm2:logs:frontend # Vite dev server logs
pnpm pm2:logs:maple # Maple tracing server logs
以非阻塞方式查看日志(最后 N 行):
pnpm exec pm2 logs lightdash-api --lines 20 --nostream
日志格式
Lightdash 日志包含用于关联的 32 字符跟踪 ID:
[700aa55e784a437aa97de9b8c5c3ed3a] GET /api/v1/health 200 - 37 ms
[4d40816e5a7d433e88f99008de5f4be5] GET /api/v1/user/login-options 200 - 2 ms
将完整的 32 字符 ID 传给 maple trace,即可查看请求。
跟踪查询(Maple CLI)
Maple 本地模式会将跟踪、日志和指标存储在内嵌的 ClickHouse 中,
并通过 maple CLI 提供访问。每条命令都可使用 --since <30m|1h|24h|7d> 指定时间范围。输出为 JSON,可通过管道传给 jq 进行筛选。
--format table 仅适用于扁平行集(services、query);其他命令会说明这一点,并回退为 JSON 输出。
每个 Shell 中只需设置一次目标服务——CLI 默认使用 4318 端口,而每个工作树 都会在其对应的槽位端口上运行自己的服务:
export MAPLE_LOCAL_URL="http://127.0.0.1:${MAPLE_PORT:-4320}"
列出服务
maple services --format table
每个服务的吞吐量、错误率和 P95 延迟。API 报告为
$LD_INSTANCE_ID(默认为 lightdash),Worker 报告为
$LD_INSTANCE_ID-scheduler。
列出最近的跟踪
maple traces --since 5m | jq '.traces[] | {traceId, spanName, durationMs}'
maple traces --since 1h --errors --min-duration-ms 500
maple slow-traces --since 15m
筛选条件:--span-name <substr>、--http-method、--errors、
--min-duration-ms、--max-duration-ms、--service、--limit。
获取跟踪详情
maple trace 700aa55e784a437aa97de9b8c5c3ed3a
返回完整的 Span 树,以及与该跟踪关联的日志。
搜索错误
maple errors --since 30m # grouped by fingerprint
maple error <fingerprint-hash> # sample traces + timeseries
搜索日志
maple logs --since 5m --severity ERROR
maple logs --trace-id 700aa55e784a437aa97de9b8c5c3ed3a
maple logs -q "compile" --since 1h
原始 SQL 备用方案
maple query "SELECT ServiceName, count() FROM traces GROUP BY ServiceName"
浏览器调试(Chrome DevTools MCP)
使用 Chrome DevTools MCP 工具进行浏览器自动化:
打开页面
mcp__chrome-devtools__new_page with url: "http://localhost:3000/login"
mcp__chrome-devtools__navigate_page with url: "http://localhost:3000", type: "url"
获取快照
mcp__chrome-devtools__take_snapshot
根据无障碍树返回页面的文本快照,并为每个元素提供唯一的 uid 标识符。
与元素交互
mcp__chrome-devtools__click with uid: "1_5"
mcp__chrome-devtools__fill with uid: "1_4", value: "[email protected]"
mcp__chrome-devtools__hover with uid: "1_3"
截屏
mcp__chrome-devtools__take_screenshot
mcp__chrome-devtools__take_screenshot with fullPage: true
mcp__chrome-devtools__take_screenshot with filePath: "/tmp/debug.png"
控制台和网络
mcp__chrome-devtools__list_console_messages
mcp__chrome-devtools__list_network_requests
mcp__chrome-devtools__get_network_request with reqid: 123
页面管理
mcp__chrome-devtools__list_pages
mcp__chrome-devtools__select_page with pageId: 1
mcp__chrome-devtools__close_page with pageId: 2
数据库检查
# Check table schema
psql -c "\d <table_name>"
# Query data directly
psql -c "SELECT * FROM <table> WHERE <condition> LIMIT 5;"
# Check scheduler jobs
psql -c "SELECT id, task_identifier, attempts, last_error FROM graphile_worker.jobs WHERE last_error IS NOT NULL LIMIT 10;"
跟踪属性(宽事件)
跟踪包含超出计时信息的上下文属性:
| 属性 | 示例 | 描述 |
|---|---|---|
http.route | /api/v1/projects/:projectUuid | 路由模式 |
http.status_code | 200 | 响应状态 |
http.method | GET | HTTP 方法 |
sentry.op | db, http.server | 操作类型 |
db.system | postgresql | 数据库类型 |
常见调试场景
| 症状 | 检查内容 |
|---|---|
| 401 未授权 | 跟踪身份验证中间件,检查会话/JWT Span |
| 403 禁止访问 | 检查跟踪属性中的用户能力/权限,查看 CASL 能力 |
| 404 未找到 | 确认路由存在,检查资源查找 Span |
| 400 错误请求 | 在验证错误或错误日志中查找问题 |
| 响应缓慢 | 检查 Span 明细,找出缓慢的 db.* 或外部调用;统计 db Span 以排查 N+1 问题 |
| 结果为空 | 验证查询参数,检查 db 查询 Span |
| 500 服务器错误 | 使用 search_errors 查找堆栈跟踪和上下文 |
| UI 未更新 | 检查 TanStack Query 开发者工具,确认变更后缓存已失效 |
| Scheduler 未运行 | 检查 pnpm pm2:logs:scheduler,查询 graphile_worker.jobs 表 |
快速命令参考
| 操作 | 命令 |
|---|---|
| 查看所有日志 | pnpm pm2:logs |
| 查看 API 日志(非阻塞) | pnpm exec pm2 logs lightdash-api --lines 20 --nostream |
| 检查进程状态 | pnpm pm2:status |
| 重启 API | pnpm pm2:restart:api |
| 最近的跟踪记录 | maple traces --since 5m |
| 跟踪记录详情 | maple trace <full-trace-id> |
| 最近的错误 | maple errors --since 5m |
| 浏览器快照 | mcp__chrome-devtools__take_snapshot |
| 打开 Maple 跟踪记录界面 | http://localhost:4320 |
跨 Agent 验证
使用另一个 AI Agent 验证你的发现、质疑你的结论,并提供独立证据。这不仅适用于你遇到困难时——在整个调试过程中都应主动寻求验证,确保分析可靠。
检测你的环境
which claude 2>/dev/null && echo "HAS_CLAUDE=true" || echo "HAS_CLAUDE=false"
which codex 2>/dev/null && echo "HAS_CODEX=true" || echo "HAS_CODEX=false"
如果你使用 Claude → 询问 Codex
codex exec "Given this context from a Lightdash debugging session:
<context>
[paste relevant logs, traces, error messages, or code snippets]
</context>
<question>
[your specific question — e.g., 'Does this trace confirm that the query cache is being bypassed?' or 'Given this stack trace, is my conclusion that the middleware short-circuits before auth correct?']
</question>"
如果你使用 Codex → 询问 Claude
claude -p "Given this context from a Lightdash debugging session:
<context>
[paste relevant logs, traces, error messages, or code snippets]
</context>
<question>
[your specific question]
</question>"
何时咨询
- 验证假设:在确定根本原因之前,询问另一个 Agent,现有证据是否支持你的理论,或是否存在其他解释
- 核实你对数据的解读:阅读跟踪记录、日志或查询输出时,确认你对内容的理解是否正确
- 质疑你的修复方案:建议修改代码之前,询问该修复是否真的解决了根本原因,还是只掩盖了症状
- 交叉检查复杂逻辑:如果问题涉及多个系统(前端 + API + 数据库 + 权限),请另一个 Agent 独立分析它们之间的交互
- 为结论提供依据:如果你准备告诉用户“X 是原因”,请先确认另一个视角也认同,或说明存在分歧
指南
- 设置 5 分钟超时:Codex 可能需要一些时间才能响应。通过 Bash 工具运行
codex exec时,将 Bash 超时设为 300000ms(5 分钟),以免过早终止。不要向codex exec本身传递--timeout——它不支持该标志。 - 具体说明:提供实际错误、跟踪输出或代码片段,不要只给出模糊描述
- 请求验证,而不只是答案:“这些证据是否支持我关于 X 的结论?”比“哪里出了问题?”更好
- 说明你当前的假设:让另一个 Agent 确认或质疑你的假设,而不是从头开始分析
- 说明你已经排除的情况:避免重复调查,让咨询聚焦于尚未解决的问题
- 务必向用户反馈:咨询另一个 Agent 后,要告知用户。总结你提出的问题、另一个 Agent 的发现,以及你是否认同其判断。如果存在分歧,呈现双方观点及各自支持证据。绝不能让用户不知道你曾咨询另一个 Agent。
测试用户凭据
用于测试需要身份验证的流程:
- 邮箱:[email protected]
- 密码:demo_password!
API 访问(个人访问令牌)
调用 API 时,优先使用此方法,而不是通过 Chrome DevTools MCP 使用 fetch()。 使用 PAT 的 curl 更快、更可靠,而且不需要浏览器会话或页面上下文。浏览器 MCP 工具仅用于 UI 交互和视觉调试。
开发环境的 PAT 会通过种子数据自动配置。使用它可以直接调用 API,无需通过浏览器登录:
# Source the env file to get LDPAT and LIGHTDASH_API_URL
source .env.development.local
# Example: list projects
curl -s -H "Authorization: ApiKey $LDPAT" "$LIGHTDASH_API_URL/api/v1/org/projects" | jq
# Example: get user info
curl -s -H "Authorization: ApiKey $LDPAT" "$LIGHTDASH_API_URL/api/v1/user" | jq
令牌(ldpat_deadbeefdeadbeefdeadbeefdeadbeef)定义在 @lightdash/common 的 SEED_PAT 中,并在数据库播种时插入。该令牌属于管理员用户([email protected]),且永不过期。