ai-engineering-init 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/code-reviewer.md +3 -130
- package/.claude/hooks/skill-forced-eval.js +2 -0
- package/.claude/hooks/stop.js +24 -1
- package/.claude/skills/codex-code-review/SKILL.md +327 -0
- package/.claude/skills/leniu-report-customization/SKILL.md +82 -2
- package/.claude/skills/leniu-report-standard-customization/SKILL.md +65 -2
- package/.claude/skills/loki-log-query/SKILL.md +400 -0
- package/.claude/skills/mysql-debug/SKILL.md +58 -22
- package/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/skill-creator/SKILL.md +479 -0
- package/.claude/skills/skill-creator/agents/analyzer.md +274 -0
- package/.claude/skills/skill-creator/agents/comparator.md +202 -0
- package/.claude/skills/skill-creator/agents/grader.md +223 -0
- package/.claude/skills/skill-creator/assets/eval_review.html +146 -0
- package/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.claude/skills/skill-creator/references/schemas.md +430 -0
- package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
- package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.claude/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.claude/skills/skill-creator/scripts/improve_description.py +248 -0
- package/.claude/skills/skill-creator/scripts/package_skill.py +136 -0
- package/.claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/.claude/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.claude/skills/skill-creator/scripts/run_loop.py +332 -0
- package/.claude/skills/skill-creator/scripts/utils.py +47 -0
- package/.claude/skills/sync-back-merge/SKILL.md +66 -0
- package/.claude/skills/yunxiao-task-management/SKILL.md +489 -0
- package/.codex/skills/leniu-report-customization/SKILL.md +82 -2
- package/.codex/skills/leniu-report-standard-customization/SKILL.md +65 -2
- package/.codex/skills/loki-log-query/SKILL.md +400 -0
- package/.codex/skills/loki-log-query/environments.json +45 -0
- package/.codex/skills/mysql-debug/SKILL.md +58 -22
- package/.codex/skills/skill-creator/LICENSE.txt +202 -0
- package/.codex/skills/skill-creator/SKILL.md +479 -0
- package/.codex/skills/skill-creator/agents/analyzer.md +274 -0
- package/.codex/skills/skill-creator/agents/comparator.md +202 -0
- package/.codex/skills/skill-creator/agents/grader.md +223 -0
- package/.codex/skills/skill-creator/assets/eval_review.html +146 -0
- package/.codex/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.codex/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.codex/skills/skill-creator/references/schemas.md +430 -0
- package/.codex/skills/skill-creator/scripts/__init__.py +0 -0
- package/.codex/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.codex/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.codex/skills/skill-creator/scripts/improve_description.py +248 -0
- package/.codex/skills/skill-creator/scripts/package_skill.py +136 -0
- package/.codex/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/.codex/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.codex/skills/skill-creator/scripts/run_loop.py +332 -0
- package/.codex/skills/skill-creator/scripts/utils.py +47 -0
- package/.codex/skills/sync-back-merge/SKILL.md +66 -0
- package/.codex/skills/yunxiao-task-management/SKILL.md +489 -0
- package/.cursor/hooks/stop.js +23 -1
- package/.cursor/skills/leniu-report-customization/SKILL.md +82 -2
- package/.cursor/skills/leniu-report-standard-customization/SKILL.md +65 -2
- package/.cursor/skills/loki-log-query/SKILL.md +400 -0
- package/.cursor/skills/loki-log-query/environments.json +45 -0
- package/.cursor/skills/mysql-debug/SKILL.md +58 -22
- package/.cursor/skills/skill-creator/LICENSE.txt +202 -0
- package/.cursor/skills/skill-creator/SKILL.md +479 -0
- package/.cursor/skills/skill-creator/agents/analyzer.md +274 -0
- package/.cursor/skills/skill-creator/agents/comparator.md +202 -0
- package/.cursor/skills/skill-creator/agents/grader.md +223 -0
- package/.cursor/skills/skill-creator/assets/eval_review.html +146 -0
- package/.cursor/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.cursor/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.cursor/skills/skill-creator/references/schemas.md +430 -0
- package/.cursor/skills/skill-creator/scripts/__init__.py +0 -0
- package/.cursor/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.cursor/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.cursor/skills/skill-creator/scripts/improve_description.py +248 -0
- package/.cursor/skills/skill-creator/scripts/package_skill.py +136 -0
- package/.cursor/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/.cursor/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.cursor/skills/skill-creator/scripts/run_loop.py +332 -0
- package/.cursor/skills/skill-creator/scripts/utils.py +47 -0
- package/.cursor/skills/sync-back-merge/SKILL.md +66 -0
- package/.cursor/skills/yunxiao-task-management/SKILL.md +489 -0
- package/bin/index.js +606 -24
- package/package.json +1 -1
- package/src/platform-map.json +4 -0
- package/src/skills/codex-code-review/SKILL.md +261 -69
- package/src/skills/leniu-report-customization/SKILL.md +82 -2
- package/src/skills/leniu-report-standard-customization/SKILL.md +65 -2
- package/src/skills/loki-log-query/SKILL.md +400 -0
- package/src/skills/loki-log-query/environments.json +45 -0
- package/src/skills/mysql-debug/SKILL.md +58 -22
- package/src/skills/skill-creator/LICENSE.txt +202 -0
- package/src/skills/skill-creator/SKILL.md +479 -0
- package/src/skills/skill-creator/agents/analyzer.md +274 -0
- package/src/skills/skill-creator/agents/comparator.md +202 -0
- package/src/skills/skill-creator/agents/grader.md +223 -0
- package/src/skills/skill-creator/assets/eval_review.html +146 -0
- package/src/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/src/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/src/skills/skill-creator/references/schemas.md +430 -0
- package/src/skills/skill-creator/scripts/__init__.py +0 -0
- package/src/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/src/skills/skill-creator/scripts/generate_report.py +326 -0
- package/src/skills/skill-creator/scripts/improve_description.py +248 -0
- package/src/skills/skill-creator/scripts/package_skill.py +136 -0
- package/src/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/src/skills/skill-creator/scripts/run_eval.py +310 -0
- package/src/skills/skill-creator/scripts/run_loop.py +332 -0
- package/src/skills/skill-creator/scripts/utils.py +47 -0
- package/src/skills/sync-back-merge/SKILL.md +66 -0
- package/src/skills/yunxiao-task-management/SKILL.md +489 -0
|
@@ -286,7 +286,69 @@ public ReportBaseTotalVO<XxxVO> pageSummary(XxxParam param) {
|
|
|
286
286
|
|
|
287
287
|
---
|
|
288
288
|
|
|
289
|
-
##
|
|
289
|
+
## 九、MySQL only_full_group_by 规范(必须遵守)
|
|
290
|
+
|
|
291
|
+
> MySQL 默认开启 `sql_mode=ONLY_FULL_GROUP_BY`,SELECT 中所有非聚合列必须出现在 GROUP BY 中,且 GROUP BY 表达式必须与 SELECT 表达式**完全一致**。
|
|
292
|
+
|
|
293
|
+
### 核心规则
|
|
294
|
+
|
|
295
|
+
**SELECT 的表达式 == GROUP BY 的表达式**(字符级别完全一致)
|
|
296
|
+
|
|
297
|
+
### ❌ 错误示例(GROUP BY 与 SELECT 不一致)
|
|
298
|
+
|
|
299
|
+
```xml
|
|
300
|
+
<!-- 报错:Expression #1 of SELECT list is not in GROUP BY clause -->
|
|
301
|
+
SELECT
|
|
302
|
+
DATE_FORMAT(roi.consume_time, '%Y-%m-%d') AS statisticDate,
|
|
303
|
+
SUM(roi.real_amount) AS totalAmount
|
|
304
|
+
FROM report_order_info roi
|
|
305
|
+
GROUP BY DATE(roi.consume_time) <!-- ❌ DATE() ≠ DATE_FORMAT(..., '%Y-%m-%d') -->
|
|
306
|
+
ORDER BY DATE(roi.consume_time)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### ✅ 正确示例(GROUP BY 与 SELECT 完全一致)
|
|
310
|
+
|
|
311
|
+
```xml
|
|
312
|
+
SELECT
|
|
313
|
+
DATE_FORMAT(roi.consume_time, '%Y-%m-%d') AS statisticDate,
|
|
314
|
+
SUM(roi.real_amount) AS totalAmount
|
|
315
|
+
FROM report_order_info roi
|
|
316
|
+
GROUP BY DATE_FORMAT(roi.consume_time, '%Y-%m-%d') <!-- ✅ 与 SELECT 完全一致 -->
|
|
317
|
+
ORDER BY DATE_FORMAT(roi.consume_time, '%Y-%m-%d') <!-- ✅ ORDER BY 也保持一致 -->
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### ❌ 错误示例(SELECT 含非聚合列未加入 GROUP BY)
|
|
321
|
+
|
|
322
|
+
```xml
|
|
323
|
+
SELECT
|
|
324
|
+
roi.canteen_id,
|
|
325
|
+
roi.canteen_name, <!-- ❌ 非聚合列未在 GROUP BY 中 -->
|
|
326
|
+
SUM(roi.real_amount) AS totalAmount
|
|
327
|
+
FROM report_order_info roi
|
|
328
|
+
GROUP BY roi.canteen_id
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### ✅ 正确示例(所有非聚合列都在 GROUP BY 中)
|
|
332
|
+
|
|
333
|
+
```xml
|
|
334
|
+
SELECT
|
|
335
|
+
roi.canteen_id,
|
|
336
|
+
roi.canteen_name,
|
|
337
|
+
SUM(roi.real_amount) AS totalAmount
|
|
338
|
+
FROM report_order_info roi
|
|
339
|
+
GROUP BY roi.canteen_id, roi.canteen_name <!-- ✅ 所有非聚合列都在 GROUP BY -->
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 检查清单
|
|
343
|
+
|
|
344
|
+
- [ ] SELECT 中按日期分组时使用 `DATE_FORMAT(col, '%Y-%m-%d')`,**不要用 `DATE()`**
|
|
345
|
+
- [ ] GROUP BY 表达式与 SELECT 中对应列**逐字相同**(复制粘贴而非重写)
|
|
346
|
+
- [ ] ORDER BY 中同样使用与 GROUP BY 一致的表达式
|
|
347
|
+
- [ ] SELECT 中所有非聚合列(无 SUM/COUNT/AVG/MAX/MIN 包裹)都出现在 GROUP BY 中
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 十、开发检查清单
|
|
290
352
|
|
|
291
353
|
### 建表
|
|
292
354
|
- [ ] 分组维度 + 金额汇总 + 审计字段(crby/crtime/upby/uptime/del_flag),无 tenant_id
|
|
@@ -302,10 +364,11 @@ public ReportBaseTotalVO<XxxVO> pageSummary(XxxParam param) {
|
|
|
302
364
|
|
|
303
365
|
### 查询
|
|
304
366
|
- [ ] ReportBaseTotalVO + CompletableFuture 并行 + MgrUserAuthPO 权限
|
|
367
|
+
- [ ] GROUP BY / ORDER BY 表达式与 SELECT 完全一致(only_full_group_by)
|
|
305
368
|
|
|
306
369
|
---
|
|
307
370
|
|
|
308
|
-
##
|
|
371
|
+
## 十一、关键代码位置
|
|
309
372
|
|
|
310
373
|
> 路径前缀均为 `core-report/.../statistics/`
|
|
311
374
|
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: loki-log-query
|
|
3
|
+
description: |
|
|
4
|
+
通过 Grafana Loki 查询线上日志,支持多环境切换、traceId 链路追踪、接口路径查询、关键词搜索。
|
|
5
|
+
|
|
6
|
+
触发场景:
|
|
7
|
+
- 需要查看线上日志排查问题
|
|
8
|
+
- 通过 traceId 查询完整请求链路
|
|
9
|
+
- 通过接口路径查询该接口的所有日志
|
|
10
|
+
- 搜索特定错误日志或异常堆栈
|
|
11
|
+
- 切换不同日志服务环境
|
|
12
|
+
|
|
13
|
+
触发词:查日志、traceId、链路追踪、Loki、日志查询、线上日志、生产日志、错误日志、异常堆栈、日志排查、切换环境、monitor
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Loki 日志查询技能
|
|
17
|
+
|
|
18
|
+
## 概述
|
|
19
|
+
|
|
20
|
+
通过 Grafana Loki API 查询线上日志。支持:
|
|
21
|
+
1. **多环境管理**:5 个 Grafana 日志服务,按需切换
|
|
22
|
+
2. **traceId 查询**:通过日志ID获取完整请求链路
|
|
23
|
+
3. **接口路径查询**:通过 API 接口路径查询相关日志
|
|
24
|
+
4. **关键词搜索**:错误信息、类名、自定义关键词
|
|
25
|
+
|
|
26
|
+
## 多环境配置
|
|
27
|
+
|
|
28
|
+
配置文件:`.claude/skills/loki-log-query/environments.json`
|
|
29
|
+
|
|
30
|
+
### 环境列表
|
|
31
|
+
|
|
32
|
+
| 环境别名 | 名称 | URL | 快捷词 |
|
|
33
|
+
|----------|------|-----|--------|
|
|
34
|
+
| `test13` | 测试13(主测试环境) | `https://test13.xnzn.net/grafana` | test13, 13 |
|
|
35
|
+
| `monitor-test` | Monitor 测试环境 | `https://monitor-test.xnzn.net/grafana` | mtest |
|
|
36
|
+
| `monitor-dev` | Monitor 开发环境 | `https://monitor-dev.xnzn.net/grafana` | mdev, dev |
|
|
37
|
+
| `monitor02-dev` | Monitor02 开发环境 | `https://monitor02-dev.xnzn.net/grafana` | m02, monitor02 |
|
|
38
|
+
| `monitor-tyy-dev` | Monitor 体验园开发环境 | `https://monitor-tyy-dev.xnzn.net/grafana` | tyy, 体验园 |
|
|
39
|
+
|
|
40
|
+
### 环境匹配规则
|
|
41
|
+
|
|
42
|
+
用户说的话 → 匹配环境:
|
|
43
|
+
- "查 test13 的日志" → `test13`
|
|
44
|
+
- "去 monitor-dev 查" → `monitor-dev`
|
|
45
|
+
- "切到体验园" → `monitor-tyy-dev`
|
|
46
|
+
- "去 m02 查一下" → `monitor02-dev`
|
|
47
|
+
- 未指定环境 → 使用 `active` 字段指定的默认环境
|
|
48
|
+
|
|
49
|
+
### 读取配置
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
SKILL_DIR="$CLAUDE_PROJECT_DIR/.claude/skills/loki-log-query"
|
|
53
|
+
ENV_FILE="${SKILL_DIR}/environments.json"
|
|
54
|
+
|
|
55
|
+
# 读取指定环境(参数: 环境别名)
|
|
56
|
+
read_env() {
|
|
57
|
+
local ENV_KEY="${1:-$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['active'])")}"
|
|
58
|
+
GRAFANA_URL=$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['environments']['${ENV_KEY}']['url'])")
|
|
59
|
+
TOKEN=$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['environments']['${ENV_KEY}']['token'])")
|
|
60
|
+
API="${GRAFANA_URL}/api/datasources/proxy/uid/loki/loki/api/v1"
|
|
61
|
+
echo "Environment: ${ENV_KEY} → ${GRAFANA_URL}"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# 通过别名查找环境 key
|
|
65
|
+
find_env() {
|
|
66
|
+
python3 -c "
|
|
67
|
+
import json
|
|
68
|
+
data = json.load(open('${ENV_FILE}'))
|
|
69
|
+
alias = '${1}'.lower()
|
|
70
|
+
for key, env in data['environments'].items():
|
|
71
|
+
if alias == key or alias in env.get('aliases', []):
|
|
72
|
+
print(key)
|
|
73
|
+
break
|
|
74
|
+
else:
|
|
75
|
+
print(data['active'])
|
|
76
|
+
"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 切换活跃环境
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# 切换默认环境为 monitor-dev
|
|
84
|
+
python3 -c "
|
|
85
|
+
import json
|
|
86
|
+
data = json.load(open('${ENV_FILE}'))
|
|
87
|
+
data['active'] = 'monitor-dev'
|
|
88
|
+
json.dump(data, open('${ENV_FILE}', 'w'), indent=2, ensure_ascii=False)
|
|
89
|
+
print('Switched to:', data['active'])
|
|
90
|
+
"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 更新 Token
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# 为某个环境设置 Token
|
|
97
|
+
python3 -c "
|
|
98
|
+
import json
|
|
99
|
+
data = json.load(open('${ENV_FILE}'))
|
|
100
|
+
data['environments']['monitor-dev']['token'] = 'glsa_新的token值'
|
|
101
|
+
json.dump(data, open('${ENV_FILE}', 'w'), indent=2, ensure_ascii=False)
|
|
102
|
+
print('Token updated for monitor-dev')
|
|
103
|
+
"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API 基础
|
|
107
|
+
|
|
108
|
+
| 项目 | 值 |
|
|
109
|
+
|------|-----|
|
|
110
|
+
| 数据源 | Loki(uid: `loki`) |
|
|
111
|
+
| **API 路径** | `{GRAFANA_URL}/api/datasources/proxy/uid/loki/loki/api/v1/query_range` |
|
|
112
|
+
| 认证 | `Authorization: Bearer {TOKEN}` |
|
|
113
|
+
| 默认标签 | `app="yunshitang"` |
|
|
114
|
+
|
|
115
|
+
> **重要**:Loki API 必须通过 Grafana datasource proxy 访问,直接 `/loki/api/v1/` 会返回 404。
|
|
116
|
+
> 查询时不限 project 标签(`{app="yunshitang"}`),可覆盖该 Grafana 下所有环境。
|
|
117
|
+
|
|
118
|
+
## 日志格式
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
2026-03-07 09:16:53.039,bcf6d955-fa26-45a5-9628-748f7ac4eed2,,, INFO 1 --- [线程名] 类名 : 行号 : 消息内容
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| 位置 | 字段 | 示例 |
|
|
125
|
+
|------|------|------|
|
|
126
|
+
| 第1段 | 时间戳 | `2026-03-07 09:16:53.039` |
|
|
127
|
+
| 第2段 | traceId | `bcf6d955-fa26-45a5-9628-748f7ac4eed2` 或 `a53dd0b0cc62bf4a79a63e77444f6f3f` |
|
|
128
|
+
| 第3段 | 商户ID | `553722740746489856` |
|
|
129
|
+
| 第4段 | 用户ID | `553723188689768448` |
|
|
130
|
+
| 第5段 | 日志内容 | `INFO 1 --- [thread] Class : 123 : msg` |
|
|
131
|
+
|
|
132
|
+
> traceId 可能是 UUID 格式(带横线)或 32位hex(不带横线),都支持。
|
|
133
|
+
|
|
134
|
+
## 查询场景
|
|
135
|
+
|
|
136
|
+
### 场景 1:按 traceId 查完整链路(最常用)
|
|
137
|
+
|
|
138
|
+
用户说:"用 `a53dd0b0cc62bf4a79a63e77444f6f3f` 查日志"
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
TRACE_ID="a53dd0b0cc62bf4a79a63e77444f6f3f"
|
|
142
|
+
END=$(date +%s)000000000
|
|
143
|
+
START=$(( $(date +%s) - 86400 ))000000000
|
|
144
|
+
|
|
145
|
+
curl -s "${API}/query_range" \
|
|
146
|
+
-H "Authorization: Bearer ${TOKEN}" \
|
|
147
|
+
--data-urlencode "query={app=\"yunshitang\"} |= \"${TRACE_ID}\"" \
|
|
148
|
+
--data-urlencode "start=${START}" \
|
|
149
|
+
--data-urlencode "end=${END}" \
|
|
150
|
+
--data-urlencode "limit=500" \
|
|
151
|
+
--data-urlencode "direction=forward" \
|
|
152
|
+
| python3 -c "
|
|
153
|
+
import sys, json
|
|
154
|
+
data = json.load(sys.stdin)
|
|
155
|
+
if data.get('status') == 'success':
|
|
156
|
+
for stream in data['data']['result']:
|
|
157
|
+
labels = stream.get('stream', {})
|
|
158
|
+
print(f'--- {labels.get(\"app\",\"?\")}/{labels.get(\"project\",\"?\")} ---')
|
|
159
|
+
for ts, line in stream['values']:
|
|
160
|
+
print(line)
|
|
161
|
+
else:
|
|
162
|
+
print('Error:', data)
|
|
163
|
+
"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 场景 2:按接口路径查日志
|
|
167
|
+
|
|
168
|
+
用户说:"查 `/security/summary/order/mealtime/classify/page` 这个接口的日志"
|
|
169
|
+
|
|
170
|
+
接口路径出现在请求日志的 `RequestLoggingFilter` 中(`>>> POST /xxx` 或 `>>> GET /xxx`)。
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
API_PATH="/security/summary/order/mealtime/classify/page"
|
|
174
|
+
END=$(date +%s)000000000
|
|
175
|
+
START=$(( $(date +%s) - 3600 ))000000000 # 最近1小时
|
|
176
|
+
|
|
177
|
+
curl -s "${API}/query_range" \
|
|
178
|
+
-H "Authorization: Bearer ${TOKEN}" \
|
|
179
|
+
--data-urlencode "query={app=\"yunshitang\"} |= \"${API_PATH}\"" \
|
|
180
|
+
--data-urlencode "start=${START}" \
|
|
181
|
+
--data-urlencode "end=${END}" \
|
|
182
|
+
--data-urlencode "limit=200" \
|
|
183
|
+
--data-urlencode "direction=forward" \
|
|
184
|
+
| python3 -c "
|
|
185
|
+
import sys, json, re
|
|
186
|
+
data = json.load(sys.stdin)
|
|
187
|
+
if data.get('status') != 'success':
|
|
188
|
+
print('Error:', data); sys.exit()
|
|
189
|
+
|
|
190
|
+
# 从匹配的请求日志中提取 traceId
|
|
191
|
+
trace_ids = set()
|
|
192
|
+
all_lines = []
|
|
193
|
+
for stream in data['data']['result']:
|
|
194
|
+
for ts, line in stream['values']:
|
|
195
|
+
all_lines.append(line)
|
|
196
|
+
# 提取 traceId(第2个逗号分隔字段)
|
|
197
|
+
parts = line.split(',')
|
|
198
|
+
if len(parts) >= 2:
|
|
199
|
+
tid = parts[1].strip()
|
|
200
|
+
if len(tid) >= 32:
|
|
201
|
+
trace_ids.add(tid)
|
|
202
|
+
|
|
203
|
+
print(f'Found {len(all_lines)} lines, {len(trace_ids)} unique traceIds')
|
|
204
|
+
print()
|
|
205
|
+
for tid in list(trace_ids)[:10]:
|
|
206
|
+
print(f' traceId: {tid}')
|
|
207
|
+
print()
|
|
208
|
+
for line in all_lines[:30]:
|
|
209
|
+
print(line)
|
|
210
|
+
if len(all_lines) > 30:
|
|
211
|
+
print(f'... and {len(all_lines)-30} more lines')
|
|
212
|
+
"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**进阶**:找到 traceId 后,再用场景 1 查该 traceId 的完整链路。
|
|
216
|
+
|
|
217
|
+
### 场景 3:关键词搜索
|
|
218
|
+
|
|
219
|
+
用户说:"搜一下 LeException" 或 "查 ERROR 日志"
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
KEYWORD="LeException"
|
|
223
|
+
END=$(date +%s)000000000
|
|
224
|
+
START=$(( $(date +%s) - 3600 ))000000000
|
|
225
|
+
|
|
226
|
+
curl -s "${API}/query_range" \
|
|
227
|
+
-H "Authorization: Bearer ${TOKEN}" \
|
|
228
|
+
--data-urlencode "query={app=\"yunshitang\"} |= \"${KEYWORD}\"" \
|
|
229
|
+
--data-urlencode "start=${START}" \
|
|
230
|
+
--data-urlencode "end=${END}" \
|
|
231
|
+
--data-urlencode "limit=100" \
|
|
232
|
+
--data-urlencode "direction=backward"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**常用关键词组合**:
|
|
236
|
+
|
|
237
|
+
```logql
|
|
238
|
+
# 所有 ERROR 日志
|
|
239
|
+
{app="yunshitang"} |= "ERROR"
|
|
240
|
+
|
|
241
|
+
# 业务异常
|
|
242
|
+
{app="yunshitang"} |= "LeException"
|
|
243
|
+
|
|
244
|
+
# SQL 错误
|
|
245
|
+
{app="yunshitang"} |~ "SQLSyntaxError|DataAccessException|BadSqlGrammar"
|
|
246
|
+
|
|
247
|
+
# 空指针
|
|
248
|
+
{app="yunshitang"} |= "NullPointerException"
|
|
249
|
+
|
|
250
|
+
# 按类名搜索
|
|
251
|
+
{app="yunshitang"} |= "OrderInfoService"
|
|
252
|
+
|
|
253
|
+
# 组合:ERROR + 特定类
|
|
254
|
+
{app="yunshitang"} |= "ERROR" |= "OrderInfoService"
|
|
255
|
+
|
|
256
|
+
# 排除健康检查噪音
|
|
257
|
+
{app="yunshitang"} |= "ERROR" != "health" != "actuator"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 场景 4:按接口查完整链路(组合查询)
|
|
261
|
+
|
|
262
|
+
用户说:"查 `/api/v2/web/order/list` 接口的全链路日志"
|
|
263
|
+
|
|
264
|
+
**两步走**:
|
|
265
|
+
1. 先按接口路径搜索,拿到 traceId
|
|
266
|
+
2. 再按 traceId 查完整链路
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Step 1: 找 traceId
|
|
270
|
+
API_PATH="/api/v2/web/order/list"
|
|
271
|
+
END=$(date +%s)000000000
|
|
272
|
+
START=$(( $(date +%s) - 3600 ))000000000
|
|
273
|
+
|
|
274
|
+
TRACE_IDS=$(curl -s "${API}/query_range" \
|
|
275
|
+
-H "Authorization: Bearer ${TOKEN}" \
|
|
276
|
+
--data-urlencode "query={app=\"yunshitang\"} |= \"${API_PATH}\" |= \">>>\"" \
|
|
277
|
+
--data-urlencode "start=${START}" \
|
|
278
|
+
--data-urlencode "end=${END}" \
|
|
279
|
+
--data-urlencode "limit=10" \
|
|
280
|
+
--data-urlencode "direction=backward" \
|
|
281
|
+
| python3 -c "
|
|
282
|
+
import sys, json
|
|
283
|
+
data = json.load(sys.stdin)
|
|
284
|
+
for stream in data.get('data',{}).get('result',[]):
|
|
285
|
+
for ts, line in stream['values']:
|
|
286
|
+
parts = line.split(',')
|
|
287
|
+
if len(parts) >= 2 and len(parts[1].strip()) >= 32:
|
|
288
|
+
print(parts[1].strip())
|
|
289
|
+
")
|
|
290
|
+
|
|
291
|
+
echo "Found traceIds:"
|
|
292
|
+
echo "${TRACE_IDS}" | head -5
|
|
293
|
+
|
|
294
|
+
# Step 2: 用第一个 traceId 查完整链路
|
|
295
|
+
FIRST_TID=$(echo "${TRACE_IDS}" | head -1)
|
|
296
|
+
if [ -n "${FIRST_TID}" ]; then
|
|
297
|
+
curl -s "${API}/query_range" \
|
|
298
|
+
-H "Authorization: Bearer ${TOKEN}" \
|
|
299
|
+
--data-urlencode "query={app=\"yunshitang\"} |= \"${FIRST_TID}\"" \
|
|
300
|
+
--data-urlencode "start=${START}" \
|
|
301
|
+
--data-urlencode "end=${END}" \
|
|
302
|
+
--data-urlencode "limit=500" \
|
|
303
|
+
--data-urlencode "direction=forward" \
|
|
304
|
+
| python3 -c "
|
|
305
|
+
import sys, json
|
|
306
|
+
data = json.load(sys.stdin)
|
|
307
|
+
for stream in data.get('data',{}).get('result',[]):
|
|
308
|
+
for ts, line in stream['values']:
|
|
309
|
+
print(line)
|
|
310
|
+
"
|
|
311
|
+
fi
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 场景 5:指定 project 环境查询
|
|
315
|
+
|
|
316
|
+
如果用户指定了具体的 project(如 test20):
|
|
317
|
+
|
|
318
|
+
```logql
|
|
319
|
+
{app="yunshitang",project="test20"} |= "traceId值"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## LogQL 语法速查
|
|
323
|
+
|
|
324
|
+
### 流选择器
|
|
325
|
+
|
|
326
|
+
| 操作 | 语法 | 示例 |
|
|
327
|
+
|------|------|------|
|
|
328
|
+
| 精确匹配 | `=` | `{app="yunshitang"}` |
|
|
329
|
+
| 不等于 | `!=` | `{app!="test"}` |
|
|
330
|
+
| 正则匹配 | `=~` | `{project=~"test2.*"}` |
|
|
331
|
+
|
|
332
|
+
### 行过滤
|
|
333
|
+
|
|
334
|
+
| 操作 | 语法 | 示例 |
|
|
335
|
+
|------|------|------|
|
|
336
|
+
| 包含 | `\|=` | `\|= "ERROR"` |
|
|
337
|
+
| 不包含 | `!=` | `!= "DEBUG"` |
|
|
338
|
+
| 正则匹配 | `\|~` | `\|~ "Exception\|Error"` |
|
|
339
|
+
| 正则排除 | `!~` | `!~ "health\|ping"` |
|
|
340
|
+
|
|
341
|
+
### 查看可用标签值
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
# 列出所有 project
|
|
345
|
+
curl -s "${API}/label/project/values" -H "Authorization: Bearer ${TOKEN}"
|
|
346
|
+
|
|
347
|
+
# 列出所有 app
|
|
348
|
+
curl -s "${API}/label/app/values" -H "Authorization: Bearer ${TOKEN}"
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Bug 修复工作流
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
1. 获取线索(traceId / 接口路径 / 错误关键词)
|
|
355
|
+
↓
|
|
356
|
+
2. 确定环境(哪个 Grafana?读取 environments.json)
|
|
357
|
+
↓
|
|
358
|
+
3. 查询日志(场景 1-4 选择合适的查询方式)
|
|
359
|
+
↓
|
|
360
|
+
4. 分析日志(ERROR/异常堆栈/SQL 错误/耗时)
|
|
361
|
+
↓
|
|
362
|
+
5. 定位代码(类名:行号 → 项目搜索)
|
|
363
|
+
↓
|
|
364
|
+
6. 修复 + 更新云效任务状态
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### 日志分析重点
|
|
368
|
+
|
|
369
|
+
1. **ERROR 级别日志** — 异常根因
|
|
370
|
+
2. **异常堆栈** — `at net.xnzn.core.xxx` 开头的行
|
|
371
|
+
3. **SQL 错误** — `SQLSyntaxErrorException`、`DataAccessException`
|
|
372
|
+
4. **业务异常** — `LeException` 抛出的位置
|
|
373
|
+
5. **耗时** — `Completed xxx in Nms`,识别慢请求
|
|
374
|
+
6. **请求/响应** — `RequestLoggingFilter` 的 `>>>` 和 `<<<` 日志
|
|
375
|
+
|
|
376
|
+
### 联动技能
|
|
377
|
+
|
|
378
|
+
| 场景 | 联动技能 |
|
|
379
|
+
|------|---------|
|
|
380
|
+
| 排查 Bug | `bug-detective` |
|
|
381
|
+
| 查询云效任务 | `yunxiao-task-management` |
|
|
382
|
+
| 查数据库 | `mysql-debug` |
|
|
383
|
+
| 性能问题 | `performance-doctor` |
|
|
384
|
+
|
|
385
|
+
## Grafana Service Account Token 创建
|
|
386
|
+
|
|
387
|
+
每个 Grafana 环境需要独立创建 Token:
|
|
388
|
+
|
|
389
|
+
1. 登录对应 Grafana URL
|
|
390
|
+
2. 左侧菜单 → Administration → Service accounts
|
|
391
|
+
3. Add service account(名称:`claude-loki-reader`),角色:`Viewer`
|
|
392
|
+
4. Add service account token → 复制
|
|
393
|
+
5. 更新 `environments.json` 中对应环境的 `token` 字段
|
|
394
|
+
|
|
395
|
+
## 注意
|
|
396
|
+
|
|
397
|
+
- 如果 Token 为空会报 `invalid API key`,需要先为该环境创建 Token
|
|
398
|
+
- 查询默认不限 project,可覆盖该 Grafana 下所有项目环境
|
|
399
|
+
- `direction=forward` 按时间正序,`direction=backward` 按时间倒序(最新优先)
|
|
400
|
+
- 如果是 Bug 排查完整流程,同时使用 `bug-detective`
|
|
@@ -27,41 +27,73 @@ description: |
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## 连接信息获取(三级降级,自动查找)
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
当本技能被激活时,**按以下优先级获取数据库连接信息**:
|
|
33
33
|
|
|
34
|
-
###
|
|
34
|
+
### 优先级 1:用户对话中指定(最高优先级)
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
用户直接给出连接信息,或指定环境名:
|
|
37
|
+
- "连 dev 环境查一下" → 使用 `.claude/mysql-config.json` 中 dev 环境的配置
|
|
38
|
+
- 直接给出 host/port/user/password → 直接使用
|
|
37
39
|
|
|
40
|
+
### 优先级 2:`.claude/mysql-config.json`(显式配置,可选)
|
|
41
|
+
|
|
42
|
+
如果文件存在且当前环境的 password 不是占位符 `YOUR_PASSWORD`,使用该配置。
|
|
43
|
+
**此文件为可选**,主要用于连接非本地环境(dev/prod 远程数据库)。
|
|
44
|
+
|
|
45
|
+
### 优先级 3:工程配置文件(零配置,本地开发默认)
|
|
46
|
+
|
|
47
|
+
从项目的 `bootstrap-dev.yml` 中自动提取连接信息:
|
|
48
|
+
|
|
49
|
+
**搜索路径**:
|
|
38
50
|
```
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
**/bootstrap-dev.yml
|
|
52
|
+
**/bootstrap*.yml(降级)
|
|
41
53
|
```
|
|
42
54
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
执行 `which mysql`,检查是否返回有效路径:
|
|
55
|
+
**解析位置**:`spring.dataset.system.master` 节点
|
|
46
56
|
|
|
57
|
+
**解析规则**:
|
|
58
|
+
```yaml
|
|
59
|
+
# 从以下字段提取:
|
|
60
|
+
jdbcUrl: jdbc:mysql://127.0.0.1:3306/system_xxx
|
|
61
|
+
username: root
|
|
62
|
+
password: xxx
|
|
47
63
|
```
|
|
48
|
-
|
|
49
|
-
|
|
64
|
+
|
|
65
|
+
**提取结果**:
|
|
66
|
+
- host = `127.0.0.1`(从 jdbcUrl 解析)
|
|
67
|
+
- port = `3306`(从 jdbcUrl 解析)
|
|
68
|
+
- user = `root`(username 字段)
|
|
69
|
+
- password = `xxx`(password 字段)
|
|
70
|
+
- 注意:jdbcUrl 中的数据库名(`system_xxx`)为系统库,**不作为查询目标数据库**。查询目标数据库 = 租户ID(从日志提取或用户指定)
|
|
71
|
+
|
|
72
|
+
### mysql CLI 查找(多路径)
|
|
73
|
+
|
|
74
|
+
按顺序查找 mysql 客户端:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
which mysql \
|
|
78
|
+
|| ls /usr/local/mysql/bin/mysql \
|
|
79
|
+
|| ls /opt/homebrew/bin/mysql \
|
|
80
|
+
|| ls /opt/homebrew/opt/mysql-client/bin/mysql
|
|
50
81
|
```
|
|
51
82
|
|
|
52
|
-
|
|
83
|
+
找到任一即可使用。如果全部不存在,输出安装提示:
|
|
84
|
+
```
|
|
85
|
+
brew install mysql-client
|
|
86
|
+
```
|
|
53
87
|
|
|
54
|
-
|
|
88
|
+
### 引导提示(仅当连接信息和 mysql CLI 都无法获取时)
|
|
55
89
|
|
|
56
90
|
```
|
|
57
91
|
⚠️ 数据库查询功能需要先完成配置:
|
|
58
92
|
|
|
59
|
-
1.
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
- user: 用户名
|
|
64
|
-
- password: 数据库密码(替换 YOUR_PASSWORD)
|
|
93
|
+
1. 连接信息获取失败:
|
|
94
|
+
- 未找到 bootstrap-dev.yml 工程配置文件
|
|
95
|
+
- 未找到 .claude/mysql-config.json 配置文件
|
|
96
|
+
→ 请创建 .claude/mysql-config.json 或确保项目中存在 bootstrap-dev.yml
|
|
65
97
|
|
|
66
98
|
2. 安装 MySQL 客户端:
|
|
67
99
|
brew install mysql-client
|
|
@@ -209,11 +241,15 @@ mysql -h 127.0.0.1 -P 3306 -u root -p'password' 546198574447230976 -e "SELECT ..
|
|
|
209
241
|
|
|
210
242
|
### 连接方式
|
|
211
243
|
|
|
212
|
-
|
|
244
|
+
按"连接信息获取"章节的优先级获取 host/port/user/password,数据库名从日志提取或用户指定:
|
|
213
245
|
|
|
214
246
|
```bash
|
|
215
|
-
#
|
|
216
|
-
|
|
247
|
+
# 连接信息来源:用户指定 > mysql-config.json > bootstrap-dev.yml 自动提取
|
|
248
|
+
# 数据库名来源:用户指定 > 日志提取的租户ID
|
|
249
|
+
mysql -h {host} -P {port} -u {user} -p'{password}' {database_from_log_or_user} -e "{SQL}"
|
|
250
|
+
|
|
251
|
+
# mysql 路径:如果 which mysql 失败,尝试绝对路径
|
|
252
|
+
/opt/homebrew/opt/mysql-client/bin/mysql -h {host} ...
|
|
217
253
|
```
|
|
218
254
|
|
|
219
255
|
### 常用排查查询模板
|