create-dwy 0.10.0 → 0.11.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/package.json +3 -2
- package/src/prompts/grouped-checkbox.js +223 -0
- package/src/sync.js +45 -19
- package/templates/claude-global/hooks/pre-git-commit-sensitive-check.sh +0 -0
- package/templates/claude-global/rules/dwy-android-core.md +1 -0
- package/templates/claude-global/rules/dwy-db-migration.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-core.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-network.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-router.md +1 -0
- package/templates/claude-global/rules/dwy-flutter-testing.md +1 -0
- package/templates/claude-global/rules/dwy-postgres.md +1 -0
- package/templates/claude-global/rules/dwy-python-backend.md +44 -3
- package/templates/claude-global/rules/dwy-python-core.md +36 -2
- package/templates/claude-global/rules/dwy-python-testing.md +1 -0
- package/templates/claude-global/rules/dwy-redis.md +1 -0
- package/templates/claude-global/rules/dwy-tdd-development.md +1 -0
- package/templates/claude-global/rules/dwy-vue-core.md +1 -0
- package/templates/claude-global/rules/dwy-vue-pinia.md +1 -0
- package/templates/claude-global/rules/dwy-vue-testing.md +1 -0
- package/templates/claude-global/skills/dwy-cibuildwheel/SKILL.md +355 -0
- package/templates/claude-global/skills/dwy-cibuildwheel/references/release-workflow-template.yml +99 -0
- package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +0 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +0 -0
- package/templates/claude-global/skills/dwy-dev-qa/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-docker/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +0 -0
- package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +0 -0
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +123 -76
- package/templates/claude-global/skills/dwy-dolphindb/references/create-database-table.md +219 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/crud-operations.md +281 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/data-import.md +271 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/docker-deployment.md +63 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/performance-tuning.md +223 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/pitfalls-and-best-practices.md +352 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/python-api.md +306 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/setup-and-connection.md +143 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/sources.md +46 -0
- package/templates/claude-global/skills/dwy-dolphindb/references/sql-batch-compute.md +206 -0
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +134 -402
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-eui/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-git-commit/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +0 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +0 -0
- package/templates/claude-global/skills/dwy-pentest/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-publish/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-tech-stack/SKILL.md +1 -0
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +0 -67
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +0 -441
- package/templates/claude-global/skills/dwy-eapi/references/health.md +0 -47
- package/templates/claude-global/skills/dwy-eapi/references/logger.md +0 -60
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dwy-mirror-source
|
|
3
3
|
description: "国内镜像源加速配置检查与修复:扫描 pip/uv/poetry/npm/pnpm/yarn/Docker/Go/Cargo/Maven/Gradle/Homebrew/Flutter 等 13 类工具的用户级和项目级配置,识别使用境外默认源或缺失配置的项,提供阿里云/清华/中科大镜像源切换。触发条件:用户说'检查镜像源'、'配置加速'、'换镜像源'、'mirror 检查'、'安装慢'、'下载慢' 时。"
|
|
4
|
+
category: 运维发布
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# 镜像源加速配置(mirror-source)
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dwy-tech-stack
|
|
3
3
|
description: 跨栈技术选型强制约束(FastAPI / Vue 3 / Flutter / UV / pnpm / Docker / S3 / DolphinDB 等)。触发场景:用户提到「用什么 ORM / 缓存 / 状态管理 / 路由 / 包管理 / 组件库」/ 引入新依赖 / 替换现有库 / 调研选型 / 新项目初始化 / 架构评估;AI 准备执行 `pnpm add` / `uv add` / `pip install` / `flutter pub add` / `npm install` 前;讨论涉及后端 Web 框架、ORM、关系数据库、缓存、消息队列、异步任务、JWT / 密码哈希、Linter;前端 Vue 框架、构建工具、组件库、状态管理、样式方案、表单校验、测试;移动端 Flutter、状态管理、HTTP 客户端、路由、本地存储、代码生成;容器化、对象存储、时序数据库时。强制:每个领域只有唯一方案,AI 不得选替代,不得「建议改用 XX」。
|
|
4
|
+
category: 脚手架
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# 跨栈技术选型强制约束
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"skill_name": "dwy-dolphindb",
|
|
3
|
-
"evals": [
|
|
4
|
-
{
|
|
5
|
-
"id": 1,
|
|
6
|
-
"prompt": "帮我写一个 Python 函数,把一个大 pandas DataFrame(约 200 万行 tick 数据)写入 DolphinDB 的 tickDB snapshots 表。表的分区列是 trade_date 和 order_book_id。函数接收 filepath(feather 格式)和 trade_date 参数。",
|
|
7
|
-
"expected_output": "生成的代码必须使用 DDBSession 复用连接、分批次 ≤500K 行写入、每批后 del + gc.collect()、用 pyarrow 分片读取、外部输入经过校验、使用 run_ddb_with_data 或 session.upload_and_run。不能出现连接池 upload、单次 append 全部数据、pd.read_feather 全量加载。",
|
|
8
|
-
"files": [],
|
|
9
|
-
"assertions": [
|
|
10
|
-
"代码中包含 BATCH_SIZE 且值 ≤ 500000",
|
|
11
|
-
"代码中使用了 gc.collect()",
|
|
12
|
-
"代码中没有使用 pd.read_feather 一次性读取大文件",
|
|
13
|
-
"代码中使用了 DDBSession 或 run_ddb_with_data 而非连接池做 upload",
|
|
14
|
-
"代码中包含对外部输入(如 trade_date、filepath)的校验逻辑"
|
|
15
|
-
]
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"id": 2,
|
|
19
|
-
"prompt": "写一个 FastAPI 端点 /api/tick/query,接收 stock_code、start_date、end_date 参数,从 DolphinDB tickDB snapshots 表查询数据并返回。请直接写实现代码。",
|
|
20
|
-
"expected_output": "生成的代码必须对输入参数校验、WHERE 条件直接命中分区列(trade_date 和 order_book_id)而不使用函数包裹、只 select 需要的列、使用 DolphinDB 分页语法 limit offset, count(而非 LIMIT OFFSET)、使用 PIVOT BY 或合并聚合查询减少往返。",
|
|
21
|
-
"files": [],
|
|
22
|
-
"assertions": [
|
|
23
|
-
"WHERE 子句中没有 date()、month()、year() 等函数包裹分区列",
|
|
24
|
-
"查询中没有 select *",
|
|
25
|
-
"分页语法是 limit {offset}, {page_size} 而不是 limit {page_size} offset {offset}",
|
|
26
|
-
"代码中包含输入校验(validate_stock_code 或类似逻辑)",
|
|
27
|
-
"没有使用 asyncio.to_thread(pool.run)"
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"id": 3,
|
|
32
|
-
"prompt": "审查以下代码是否符合 DolphinDB 开发规范,指出所有问题并给出修复后的代码:\n```python\nasync def import_tick_data(filepath: str, trade_date: str):\n df = pd.read_feather(filepath)\n script = f'append!{loadTable(\"dfs://tickDB\", \"snapshots\"), data}'\n await run_ddb(f'upload data = {df}')\n await run_ddb(script)\n return {\"status\": \"ok\"}\n\nasync def query_ticks(stock_code: str, start: str, end: str):\n script = (\n f'select * from loadTable(\"dfs://tickDB\", \"snapshots\") '\n f'where date(trade_date) >= {start} and date(trade_date) <= {end} '\n f'and order_book_id = \"{stock_code}\" '\n f'limit 100 offset 0'\n )\n return await run_ddb(script)\n```",
|
|
33
|
-
"expected_output": "审查报告必须指出:1) pd.read_feather 全量加载问题;2) 连接池 upload + run 导致变量跨连接不可见;3) 未校验 filepath、trade_date、stock_code;4) date(trade_date) 函数包裹分区列导致全表扫描;5) select *;6) limit 100 offset 0 语法错误;7) 缺少 BATCH_SIZE 和 gc.collect()。每项必须给出修复代码。",
|
|
34
|
-
"files": [],
|
|
35
|
-
"assertions": [
|
|
36
|
-
"输出中明确指出了 date(trade_date) 或分区列被函数包裹的问题",
|
|
37
|
-
"输出中明确指出了 select * 的问题",
|
|
38
|
-
"输出中明确指出了 limit 100 offset 0 语法错误",
|
|
39
|
-
"输出中明确指出了连接池 upload + run 的问题",
|
|
40
|
-
"输出中为每个问题都提供了修复后的代码片段"
|
|
41
|
-
]
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"id": 4,
|
|
45
|
-
"prompt": "审查以下 DolphinDB 查询脚本是否能触发分区剪枝,并给出修复后的脚本:\n```sql\nselect * from loadTable(\"dfs://tickDB\", \"snapshots\")\nwhere 2022.12.01 <= TradeDate <= 2022.12.03\n and order_book_id = \"000001.SZ\"\n```",
|
|
46
|
-
"expected_output": "审查必须指出:1) `2022.12.01 <= TradeDate <= 2022.12.03` 是链式比较,DolphinDB 不识别两端边界,无法做分区剪枝,会扫描全部分区;2) 给出修复方案:改为 `TradeDate between 2022.12.01 and 2022.12.03` 或拆成 `TradeDate >= 2022.12.01 and TradeDate <= 2022.12.03`;3) 同时指出 select * 浪费带宽,应只取需要的列。",
|
|
47
|
-
"files": [],
|
|
48
|
-
"assertions": [
|
|
49
|
-
"输出中明确指出了链式比较无法触发分区剪枝",
|
|
50
|
-
"输出中提供了 between ... and ... 或拆分为两个独立条件的修复方案",
|
|
51
|
-
"输出中提到了 select * 的问题"
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"id": 5,
|
|
56
|
-
"prompt": "帮我设计一张存储股票 tick 数据的 DolphinDB 分布式表,每天约 5000 万条记录,覆盖国内 5000 只股票,使用 TSDB 引擎。给出分区方案并说明理由。",
|
|
57
|
-
"expected_output": "方案必须:1) 一级 VALUE 分区按 TradeDate,二级用 HASH 分区按 SecurityID(约 20 桶),而不是 VALUE 分区,避免每天 5000+ 个小分区命中 maxPartitionNumPerQuery=65536 限制;2) 估算单分区大小(每天 3GB / 20 桶 ≈ 150MB,落在 TSDB 推荐区间 400MB~1GB 之下但仍可接受,或调整桶数让单分区更接近 400MB);3) 提醒分区方案一经确定无法调整;4) 提醒 SYMBOL 列 SecurityID 写入前需校验 ≤255 字节。",
|
|
58
|
-
"files": [],
|
|
59
|
-
"assertions": [
|
|
60
|
-
"方案中二级分区使用 HASH 而非 VALUE",
|
|
61
|
-
"方案中包含单分区大小估算并参考 TSDB 100MB~1GB 推荐区间",
|
|
62
|
-
"方案中提到 maxPartitionNumPerQuery=65536 或避免分区数爆炸",
|
|
63
|
-
"方案中提到 SYMBOL 列长度校验或 ≤255 字节限制"
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
}
|
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
# DolphinDB 开发规则
|
|
2
|
-
|
|
3
|
-
所有涉及 DolphinDB 的代码必须遵循以下规则。AI 生成或修改代码时,必须逐条检查。
|
|
4
|
-
|
|
5
|
-
## 一、社区版硬约束
|
|
6
|
-
|
|
7
|
-
| 约束 | 值 | 违反后果 |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| 最大内存 | 8GB(建议 `maxMemSize=5`,留 3GB 给 OS) | 进程 OOM 崩溃 |
|
|
10
|
-
| 最大集群节点数 | 2(允许 1 controller + 1 datanode 的最小集群) | 超出被许可证拒绝;无法扩展到 3 节点以上 |
|
|
11
|
-
| 最大并发客户端连接 | 512(`maxConnections=512`) | 新连接直接被拒 |
|
|
12
|
-
| TSDB 单次 append | ≤ 204MB(约 800K 行 × 30 列) | 报错 `exceeds max limit` |
|
|
13
|
-
| 高可用 / Raft / 异步复制 | 不支持 | 多副本能力需企业版 |
|
|
14
|
-
|
|
15
|
-
### 本项目部署侧配置(非社区版硬限,是 VM/机器约束)
|
|
16
|
-
|
|
17
|
-
- VM 规格:**2 核 8 GB**(与社区版 8 GB 内存硬限一致)
|
|
18
|
-
- 因 CPU 只有 2 核,`workerNum` 和客户端 `dolphindb_pool_size` 按经验值 `2`,避免大量并行争抢 CPU;**这不是社区版硬限,而是 2 核机器上的合理取值**
|
|
19
|
-
- 512 并发连接是协议层上限,实际可持有的连接远少于这个数,不要把它当作"应该开 512 条"的指令
|
|
20
|
-
|
|
21
|
-
### 违规检测
|
|
22
|
-
如果代码中出现以下情况,必须提示:
|
|
23
|
-
- 单次 `append!` 数据超过 500K 行(安全上限)
|
|
24
|
-
- `maxMemSize` > 8
|
|
25
|
-
- 集群配置超过 2 个节点
|
|
26
|
-
- 客户端 `dolphindb_pool_size` 显著高于 CPU 核数(在 2 核机器上 > 4 需说明理由)
|
|
27
|
-
|
|
28
|
-
## 二、连接与会话管理
|
|
29
|
-
|
|
30
|
-
### 强制规则
|
|
31
|
-
|
|
32
|
-
| 场景 | 使用方式 | 原因 |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| 只读查询 | `run_ddb(script)` — 连接池 | 池自动管理连接复用 |
|
|
35
|
-
| 单次 upload + run | `run_ddb_with_data(script, data)` — 独立 Session | 确保 upload 和 run 在同一连接 |
|
|
36
|
-
| 批量 upload(多次循环) | `DDBSession` — 复用 Session | 避免每批新建/关闭连接的开销 |
|
|
37
|
-
|
|
38
|
-
```python
|
|
39
|
-
# ❌ 用连接池做 upload + run(变量跨连接不可见)
|
|
40
|
-
await run_ddb("upload_var = ...") # 连接 A
|
|
41
|
-
await run_ddb("select * from upload_var") # 可能路由到连接 B → 变量不存在
|
|
42
|
-
|
|
43
|
-
# ✅ 独立 Session 保证同一连接
|
|
44
|
-
await run_ddb_with_data(script, {"batch_data": df})
|
|
45
|
-
|
|
46
|
-
# ✅ 批量操作复用 Session
|
|
47
|
-
session = DDBSession()
|
|
48
|
-
try:
|
|
49
|
-
for batch in batches:
|
|
50
|
-
await session.upload_and_run(script, {"batch_data": batch})
|
|
51
|
-
finally:
|
|
52
|
-
session.close()
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### SDK v3 异步 API
|
|
56
|
-
- `DBConnectionPool.run()` 在 SDK v3 中是 **原生 async**,直接 `await pool.run(script)`
|
|
57
|
-
- **禁止** `asyncio.to_thread(pool.run, script)`(会产生 "coroutine never awaited" 警告)
|
|
58
|
-
- `Session.run()` 仍然是 **同步**,需要 `asyncio.to_thread()` 包装
|
|
59
|
-
|
|
60
|
-
## 三、分区裁剪(性能关键)
|
|
61
|
-
|
|
62
|
-
### 强制规则
|
|
63
|
-
**WHERE 条件必须直接命中分区列,禁止对分区列使用函数。**
|
|
64
|
-
|
|
65
|
-
```python
|
|
66
|
-
# ❌ 函数包裹分区列 → 全表扫描 → 8GB OOM
|
|
67
|
-
WHERE date(datetime) = 2020.01.01
|
|
68
|
-
WHERE month(trade_date) = 2020.01M
|
|
69
|
-
WHERE string(order_book_id) = "000001.XSHE"
|
|
70
|
-
|
|
71
|
-
# ✅ 直接范围过滤 → 分区裁剪生效
|
|
72
|
-
WHERE trade_date = 2020.01.01
|
|
73
|
-
WHERE trade_date >= 2020.01.01, trade_date < 2020.02.01
|
|
74
|
-
WHERE order_book_id in [`000001.XSHE`000002.XSHE]
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 范围查询禁止链式过滤
|
|
78
|
-
|
|
79
|
-
DolphinDB 不识别链式比较的两端边界,无法做分区剪枝。必须用 `between ... and ...` 或拆成两个独立条件。
|
|
80
|
-
|
|
81
|
-
```sql
|
|
82
|
-
-- ❌ 链式过滤 → 全表扫描(实测扫描 10091 个分区)
|
|
83
|
-
select * from loadTable(dbName, tbName)
|
|
84
|
-
where 2022.12.01 <= TradeDate <= 2022.12.03
|
|
85
|
-
|
|
86
|
-
-- ✅ between ... and ... → 分区剪枝(实测扫描 90 个分区)
|
|
87
|
-
select * from loadTable(dbName, tbName)
|
|
88
|
-
where TradeDate between 2022.12.01 and 2022.12.03
|
|
89
|
-
|
|
90
|
-
-- ✅ 拆成两个独立条件 → 分区剪枝
|
|
91
|
-
select * from loadTable(dbName, tbName)
|
|
92
|
-
where TradeDate >= 2022.12.01 and TradeDate <= 2022.12.03
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
可用 `[HINT_EXPLAIN]` 查看实际扫描的分区数验证:`select [HINT_EXPLAIN] * from ...` 输出中的 `partitions.local` 数量应等于范围内的分区数,而不是全库分区数。
|
|
96
|
-
|
|
97
|
-
### tickDB 分区列
|
|
98
|
-
- 第一级:`trade_date`(VALUE 分区,按日)
|
|
99
|
-
- 第二级:`order_book_id`(HASH 分区,20 桶)
|
|
100
|
-
|
|
101
|
-
### factorDB 分区列
|
|
102
|
-
- 第一级:`trade_date`(VALUE 分区,按月)
|
|
103
|
-
- 第二级:`factor_name`(VALUE 分区)
|
|
104
|
-
|
|
105
|
-
### 违规检测
|
|
106
|
-
如果 DolphinDB 查询脚本中出现以下模式,必须提示:
|
|
107
|
-
- `date(...)` / `month(...)` / `year(...)` 包裹分区列
|
|
108
|
-
- `WHERE 1=1` 无分区条件(全表扫描)
|
|
109
|
-
- `select * from loadTable(...)` 无 WHERE 子句
|
|
110
|
-
- 链式比较 `a <= col <= b`(必须改为 `between a and b` 或拆成两个独立条件)
|
|
111
|
-
|
|
112
|
-
## 四、批量写入
|
|
113
|
-
|
|
114
|
-
### 强制规则
|
|
115
|
-
|
|
116
|
-
```python
|
|
117
|
-
BATCH_SIZE = 500_000 # TSDB 上限 204MB,500K 行 ≈ 120MB(安全余量)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
| 规则 | 说明 |
|
|
121
|
-
|---|---|
|
|
122
|
-
| 每批 ≤ 500K 行 | TSDB 单次 append 内存上限 204MB |
|
|
123
|
-
| 每批后 `gc.collect()` | 释放 Python 侧内存 |
|
|
124
|
-
| 每批后 `del batch_df` | 显式释放 DataFrame 引用 |
|
|
125
|
-
| 大文件用 pyarrow 分片读取 | `pf.read_table()` + `table.slice()` 避免全量加载 |
|
|
126
|
-
| 复用 Session | 批量循环中不要每批新建连接 |
|
|
127
|
-
|
|
128
|
-
```python
|
|
129
|
-
# ✅ 标准批量上传模式
|
|
130
|
-
table = pf.read_table(filepath) # memory-mapped,不全量加载
|
|
131
|
-
session = DDBSession()
|
|
132
|
-
try:
|
|
133
|
-
for i in range(num_batches):
|
|
134
|
-
arrow_slice = table.slice(i * BATCH_SIZE, BATCH_SIZE)
|
|
135
|
-
batch_df = prepare_batch(arrow_slice.to_pandas())
|
|
136
|
-
del arrow_slice
|
|
137
|
-
await session.upload_and_run(script, {"batch_data": batch_df})
|
|
138
|
-
del batch_df
|
|
139
|
-
gc.collect()
|
|
140
|
-
finally:
|
|
141
|
-
session.close()
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 禁止的写法
|
|
145
|
-
```python
|
|
146
|
-
# ❌ 一次性读取大文件到 pandas(OOM)
|
|
147
|
-
df = pd.read_feather("10M_rows.ftr") # 2.6GB 内存
|
|
148
|
-
|
|
149
|
-
# ❌ 单次 append 超过 500K 行
|
|
150
|
-
await run_ddb_with_data(script, {"data": df_1million_rows})
|
|
151
|
-
|
|
152
|
-
# ❌ 不释放内存
|
|
153
|
-
for batch in batches:
|
|
154
|
-
await session.upload_and_run(script, {"data": batch})
|
|
155
|
-
# 忘记 del + gc.collect()
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## 五、DolphinDB 脚本语法
|
|
159
|
-
|
|
160
|
-
### 列名定义必须单行
|
|
161
|
-
```python
|
|
162
|
-
# ❌ 多行 backtick 列名 → 语法错误
|
|
163
|
-
schema = table(1:0,
|
|
164
|
-
`col1`col2
|
|
165
|
-
`col3`col4,
|
|
166
|
-
[INT, INT, INT, INT])
|
|
167
|
-
|
|
168
|
-
# ✅ 单行定义
|
|
169
|
-
colNames = `col1`col2`col3`col4
|
|
170
|
-
colTypes = [INT, INT, INT, INT]
|
|
171
|
-
schema = table(1:0, colNames, colTypes)
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### 多值过滤的 backtick 拼接
|
|
175
|
-
```python
|
|
176
|
-
# ✅ 正确的 Symbol 列表拼接
|
|
177
|
-
symbols = ["000001.XSHE", "000002.XSHE"]
|
|
178
|
-
symbols_str = "`" + "`".join(symbols)
|
|
179
|
-
script = f"... order_book_id in [{symbols_str}]"
|
|
180
|
-
# 生成: order_book_id in [`000001.XSHE`000002.XSHE]
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### 脚本拼接用分号 + 换行
|
|
184
|
-
```python
|
|
185
|
-
# ✅ 多语句用 ;\n 分隔
|
|
186
|
-
script = (
|
|
187
|
-
f'tickData = select * from loadTable("dfs://tickDB","snapshots") '
|
|
188
|
-
f'where {ts_filter};\n'
|
|
189
|
-
f'{user_script};\n'
|
|
190
|
-
f'exec count(*) from result'
|
|
191
|
-
)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## 六、输入校验(安全关键)
|
|
195
|
-
|
|
196
|
-
### 强制规则
|
|
197
|
-
**所有外部输入在拼接到 DolphinDB 脚本前,必须经过 `validators.py` 校验。**
|
|
198
|
-
|
|
199
|
-
| 类型 | 正则 | 示例 |
|
|
200
|
-
|---|---|---|
|
|
201
|
-
| 因子名 | `^[a-zA-Z0-9_]{1,50}$` | `alpha_vwap_001` |
|
|
202
|
-
| 股票代码 (ukey) | `^\d{6}\.[A-Z]{2}$` | `000001.SZ` |
|
|
203
|
-
| 日期 | `^\d{4}\.\d{2}\.\d{2}$` | `2024.01.02` |
|
|
204
|
-
|
|
205
|
-
```python
|
|
206
|
-
# ✅ 先校验再拼接
|
|
207
|
-
validate_factor_name(factor_name) # 不通过则抛 BusinessError
|
|
208
|
-
script = f'... factor_name = "{factor_name}"'
|
|
209
|
-
|
|
210
|
-
# ❌ 未校验直接拼接 → 脚本注入风险
|
|
211
|
-
script = f'... factor_name = "{user_input}"'
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### 违规检测
|
|
215
|
-
如果代码中出现以下情况,必须提示:
|
|
216
|
-
- f-string 拼接 DolphinDB 脚本时使用了未经 `validate_*` 校验的变量
|
|
217
|
-
- 用户输入直接出现在 `run_ddb()` 的 script 参数中
|
|
218
|
-
|
|
219
|
-
## 七、分区操作
|
|
220
|
-
|
|
221
|
-
### 删除分区用 `dropPartition`,不用 `DELETE`
|
|
222
|
-
```python
|
|
223
|
-
# ✅ dropPartition — 原子操作,无 tombstone
|
|
224
|
-
dropPartition(db, partitionPath, tableName)
|
|
225
|
-
|
|
226
|
-
# ❌ DELETE — 标记删除,产生 tombstone,需后续 GC
|
|
227
|
-
delete from loadTable("dfs://tickDB", "snapshots") where trade_date = 2020.01.01
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### 新增分区值
|
|
231
|
-
配置 `newValuePartitionPolicy=add`,VALUE 分区自动扩展,无需预创建。
|
|
232
|
-
|
|
233
|
-
## 八、查询优化
|
|
234
|
-
|
|
235
|
-
### 聚合查询合并
|
|
236
|
-
```python
|
|
237
|
-
# ❌ 3 次查询
|
|
238
|
-
min_date = await run_ddb("select min(trade_date) ...")
|
|
239
|
-
max_date = await run_ddb("select max(trade_date) ...")
|
|
240
|
-
count = await run_ddb("select count(*) ...")
|
|
241
|
-
|
|
242
|
-
# ✅ 1 次查询
|
|
243
|
-
result = await run_ddb(
|
|
244
|
-
'select min(trade_date) as earliest, max(trade_date) as latest, count(*) as total '
|
|
245
|
-
'from loadTable("dfs://tickDB","snapshots")'
|
|
246
|
-
)
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### 多因子查询用 PIVOT BY
|
|
250
|
-
```python
|
|
251
|
-
# ✅ DolphinDB 服务端完成列转换,无需 Python 端 merge
|
|
252
|
-
script = (
|
|
253
|
-
'select value from loadTable("dfs://factorDB","factors") '
|
|
254
|
-
f'where {where_clause} '
|
|
255
|
-
'pivot by trade_date, ukey, factor_name'
|
|
256
|
-
)
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### 分页查询用 `limit offset, count`
|
|
260
|
-
```python
|
|
261
|
-
# ❌ SQL 标准语法 — DolphinDB 不支持 OFFSET 关键字
|
|
262
|
-
f"select * from loadTable(...) limit {page_size} offset {offset}"
|
|
263
|
-
|
|
264
|
-
# ✅ DolphinDB 分页语法:limit 跳过行数, 返回行数
|
|
265
|
-
offset = (page - 1) * page_size
|
|
266
|
-
f"select * from loadTable(...) limit {offset}, {page_size}"
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### 只查需要的列
|
|
270
|
-
```python
|
|
271
|
-
# ❌ select *(传输全部 30 列)
|
|
272
|
-
"select * from loadTable(...)"
|
|
273
|
-
|
|
274
|
-
# ✅ 只取需要的列
|
|
275
|
-
"select trade_date, time, order_book_id, last, volume from loadTable(...)"
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## 九、Docker 环境注意事项
|
|
279
|
-
|
|
280
|
-
### DolphinDB v3 Alpine 镜像
|
|
281
|
-
| 问题 | 解决方案 |
|
|
282
|
-
|---|---|
|
|
283
|
-
| 无 `curl` | 用 `wget` 替代 |
|
|
284
|
-
| `localhost` 解析到 IPv6 | healthcheck 用 `127.0.0.1` |
|
|
285
|
-
| macOS 无 `/etc/hostname` `/etc/machine-id` | 不要挂载这两个文件 |
|
|
286
|
-
| 自定义 cfg 中 `logFile`/`volumes` 路径与 Docker volume 冲突 | 只覆盖必要参数,其余用默认值 |
|
|
287
|
-
| `dataSync + CacheEngine` 必须同时启用 | 开发环境建议都不配置,用默认值 |
|
|
288
|
-
|
|
289
|
-
### healthcheck 配置
|
|
290
|
-
```yaml
|
|
291
|
-
healthcheck:
|
|
292
|
-
test: ["CMD-SHELL", "wget -qO- --timeout=3 http://127.0.0.1:8848/ || exit 1"]
|
|
293
|
-
start_period: 30s # DolphinDB 启动较慢,给足初始化时间
|
|
294
|
-
interval: 5s
|
|
295
|
-
timeout: 5s
|
|
296
|
-
retries: 10
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### 后端连接配置
|
|
300
|
-
```
|
|
301
|
-
DOLPHINDB_HOST=dolphindb # Docker 服务名(不是 localhost)
|
|
302
|
-
DOLPHINDB_PORT=8848
|
|
303
|
-
DOLPHINDB_POOL_SIZE=2 # 2 核机器的经验值;社区版协议上限是 512 并发,不是 2
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## 十、数据类型映射
|
|
307
|
-
|
|
308
|
-
### Feather/Pandas → DolphinDB
|
|
309
|
-
|
|
310
|
-
| Python/Pandas 类型 | DolphinDB 类型 | 转换方式 |
|
|
311
|
-
|---|---|---|
|
|
312
|
-
| `uint32` (日期如 20191205) | `DATE` | `pd.to_datetime(str, format='%Y%m%d')` |
|
|
313
|
-
| `uint32` (时间如 91509000) | `INT` | `.astype('int64')` |
|
|
314
|
-
| `float64` | `DOUBLE` | 无需转换 |
|
|
315
|
-
| `uint64` | `LONG` | `.astype('int64')` |
|
|
316
|
-
| `uint32` | `INT` | `.astype('int64')` |
|
|
317
|
-
| `float64` 含 NaN → 整数 | `INT` | `.fillna(0).astype('int64')` |
|
|
318
|
-
| `object` (字符串) | `SYMBOL` | `.astype(str)` |
|
|
319
|
-
|
|
320
|
-
### 注意事项
|
|
321
|
-
- DolphinDB 不接受 `uint` 类型,必须先转 `int64`
|
|
322
|
-
- NaN 不能直接转整数,必须先 `fillna(0)`
|
|
323
|
-
- `SYMBOL` 类型是 DolphinDB 的字典编码字符串,适用于低基数列(如股票代码)
|
|
324
|
-
|
|
325
|
-
## 十一、测试 Mock 规范
|
|
326
|
-
|
|
327
|
-
### DolphinDB 测试不连真实服务
|
|
328
|
-
```python
|
|
329
|
-
# conftest.py 中使用 autouse fixture mock 所有 DolphinDB 调用
|
|
330
|
-
@pytest.fixture(autouse=True)
|
|
331
|
-
def mock_dolphindb():
|
|
332
|
-
async def mock_run_ddb(script):
|
|
333
|
-
if "snapshots" in script and "count" in script.lower():
|
|
334
|
-
return pd.DataFrame({"count": [1000]})
|
|
335
|
-
# ... 基于脚本内容的模式匹配
|
|
336
|
-
return pd.DataFrame()
|
|
337
|
-
|
|
338
|
-
with patch("app.services.tick_service.run_ddb", side_effect=mock_run_ddb):
|
|
339
|
-
yield
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### Mock 必须覆盖所有 import 路径
|
|
343
|
-
```python
|
|
344
|
-
# 每个 service 文件都有独立的 import,必须分别 patch
|
|
345
|
-
patch("app.services.tick_service.run_ddb", ...)
|
|
346
|
-
patch("app.services.tick_archive.run_ddb", ...)
|
|
347
|
-
patch("app.services.tick_compute.run_ddb", ...)
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
## 十二、分区设计原则(建表时)
|
|
351
|
-
|
|
352
|
-
### 单分区合理大小(压缩前)
|
|
353
|
-
|
|
354
|
-
| 引擎 | 推荐范围 | 说明 |
|
|
355
|
-
|---|---|---|
|
|
356
|
-
| OLAP | 100MB ~ 300MB | 列存按分区粒度加载,过大易 OOM |
|
|
357
|
-
| TSDB / PKEY | 400MB ~ 1GB | 内部还有 LevelFile 二级组织,单分区可大一些 |
|
|
358
|
-
|
|
359
|
-
分区过小:元数据膨胀、查询时分区数爆炸、命中 `maxPartitionNumPerQuery` 限制(默认 65536)。
|
|
360
|
-
分区过大:单分区加载内存压力大、并行度下降、删除/重写代价高。
|
|
361
|
-
|
|
362
|
-
### 分区层级与类型
|
|
363
|
-
|
|
364
|
-
| 维度 | 规则 |
|
|
365
|
-
|---|---|
|
|
366
|
-
| 层级 | 最少 1 级,最多 3 级;分区方案一经确定无法调整,库下所有表共用 |
|
|
367
|
-
| 范围分区 (RANGE) | **只能向后扩展且无法自动扩展**,需手动 `addRangePartitions` |
|
|
368
|
-
| 范围分区越界数据 | `allowMissingPartitions=true`(默认)会**静默丢弃**范围外数据;需要报错改为 `false` |
|
|
369
|
-
| 值分区 (VALUE) | 可自动扩展(需 `newValuePartitionPolicy=add`);**不宜初始化过多空分区** |
|
|
370
|
-
| 哈希分区 (HASH) | 桶数固定,建库后不可改 |
|
|
371
|
-
|
|
372
|
-
### 分区方案设计反例
|
|
373
|
-
|
|
374
|
-
> 股票行情数据(每天 5000 万条记录),先按 `TradeDate` 值分区,再按 `SecurityID` 二级值分区。
|
|
375
|
-
> 假设全是国内股票(约 5000 只),一天分区数就达 5000+。每天数据 3GB,**每个分区只有约 0.6MB**。
|
|
376
|
-
|
|
377
|
-
后果:
|
|
378
|
-
- 默认 `maxPartitionNumPerQuery=65536`,每天 5000 个分区,**查 14 天就报错**:`The number of partitions relevant to the query is too large`
|
|
379
|
-
- 单分区 0.6MB 远低于 OLAP 100MB / TSDB 400MB 推荐下限,元数据开销远大于数据本身
|
|
380
|
-
- 修复方向:二级分区改用 `HASH(SecurityID, 20)`(按经验固定 20 桶),或合并到日粒度单级分区
|
|
381
|
-
|
|
382
|
-
### 设计检查项
|
|
383
|
-
|
|
384
|
-
- [ ] 单分区估算大小是否落在引擎推荐区间?
|
|
385
|
-
- [ ] 总分区数是否远低于单次查询 65536 上限?
|
|
386
|
-
- [ ] 二级是否用了高基数列做 VALUE 分区?(高基数列优先 HASH)
|
|
387
|
-
- [ ] RANGE 分区是否预留了 `addRangePartitions` 的扩展机制?是否需要 `allowMissingPartitions=false`?
|
|
388
|
-
|
|
389
|
-
## 十三、字符串/BLOB/SYMBOL 长度限制
|
|
390
|
-
|
|
391
|
-
自 **1.30.23 / 2.00.11** 版本起,分布式表写入对字符串类数据增加了硬性大小限制:
|
|
392
|
-
|
|
393
|
-
| 类型 | 上限 | 超限行为 |
|
|
394
|
-
|---|---|---|
|
|
395
|
-
| `STRING` | < 64 KB | 静默截断到 65,535 字节(64 KB - 1) |
|
|
396
|
-
| `BLOB` | < 64 MB | 静默截断到 67,108,863 字节(64 MB - 1) |
|
|
397
|
-
| `SYMBOL` | ≤ 255 字节 | **直接抛异常**,写入失败 |
|
|
398
|
-
|
|
399
|
-
### 强制规则
|
|
400
|
-
|
|
401
|
-
写入前必须做长度校验,**特别是 SYMBOL 列会让整批写入失败**:
|
|
402
|
-
|
|
403
|
-
```python
|
|
404
|
-
# ✅ 写入前校验 SYMBOL 列长度
|
|
405
|
-
MAX_SYMBOL_BYTES = 255
|
|
406
|
-
oversize_mask = batch_df["order_book_id"].str.encode("utf-8").str.len() > MAX_SYMBOL_BYTES
|
|
407
|
-
if oversize_mask.any():
|
|
408
|
-
raise BusinessError(
|
|
409
|
-
f"SYMBOL 列 order_book_id 有 {oversize_mask.sum()} 行超过 255 字节"
|
|
410
|
-
)
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### 类型选择建议
|
|
414
|
-
|
|
415
|
-
- 短编码字段(股票代码、因子名)→ `SYMBOL`,但必须保证 ≤ 255 字节
|
|
416
|
-
- 用户可输入的中等长度文本(备注、描述)→ `STRING`,注意 64 KB 截断风险,超长字段做应用层校验
|
|
417
|
-
- 大文本/二进制(JSON、文件)→ `BLOB`,超长会被截断而不是报错,**截断不可逆**,必须在应用层校验长度
|
|
418
|
-
- 中文按 UTF-8 编码,1 字符 ≈ 3 字节,预估时按字节而非字符
|
|
419
|
-
|
|
420
|
-
### 违规检测
|
|
421
|
-
|
|
422
|
-
- 写入路径上对 `SYMBOL` 列没有长度校验 → 整批写入可能失败
|
|
423
|
-
- 写入 `STRING`/`BLOB` 时没有长度上限断言 → 数据被静默截断,事后无法恢复
|
|
424
|
-
|
|
425
|
-
## 十四、检查清单
|
|
426
|
-
|
|
427
|
-
每次涉及 DolphinDB 的代码变更,必须逐条检查:
|
|
428
|
-
|
|
429
|
-
- [ ] WHERE 条件是否命中分区列?是否有函数包裹分区列?
|
|
430
|
-
- [ ] 范围条件是否使用 `between ... and ...` 或两个独立条件?是否存在链式比较 `a <= col <= b`?
|
|
431
|
-
- [ ] 批量写入是否 ≤ 500K 行?是否有 `gc.collect()`?
|
|
432
|
-
- [ ] 外部输入是否经过 `validators.py` 校验?
|
|
433
|
-
- [ ] 使用 `run_ddb` 还是 `run_ddb_with_data`?场景是否匹配?
|
|
434
|
-
- [ ] 连接池大小是否与 CPU 核数匹配(2 核机器建议 `pool_size=2`)?
|
|
435
|
-
- [ ] 查询是否只 select 了需要的列?
|
|
436
|
-
- [ ] 多次聚合查询能否合并为一次?
|
|
437
|
-
- [ ] 脚本中 backtick 列名是否在单行内?
|
|
438
|
-
- [ ] 新增 service 文件的 `run_ddb` 导入是否在 conftest.py 中 mock?
|
|
439
|
-
- [ ] 分页查询是否使用 `limit offset, count` 语法?(禁止 `LIMIT x OFFSET y`)
|
|
440
|
-
- [ ] 新建库表时单分区估算大小是否在引擎推荐区间内?总分区数是否远低于 65536?
|
|
441
|
-
- [ ] 写入路径对 `SYMBOL` 列是否有 ≤ 255 字节长度校验?`STRING` / `BLOB` 是否有上限断言以避免静默截断?
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# health — 健康检查路由工厂
|
|
2
|
-
|
|
3
|
-
> 何时读这份:当用户需要添加健康检查端点、配置 k8s livenessProbe、或讨论 readiness vs liveness 设计时读取。
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from dwyeapi.health import create_health_router
|
|
7
|
-
|
|
8
|
-
router = create_health_router(
|
|
9
|
-
service_name="my-api",
|
|
10
|
-
version="1.0.0",
|
|
11
|
-
path="/health",
|
|
12
|
-
include_in_schema=True,
|
|
13
|
-
)
|
|
14
|
-
app.include_router(router)
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## 设计取舍
|
|
18
|
-
|
|
19
|
-
端点**只**报告进程是否存活(liveness),**不连接 PostgreSQL / Redis** 等依赖。
|
|
20
|
-
|
|
21
|
-
原因:健康端点通常对公网开放,若内置 readiness / dependency check,攻击者可用低频高并发请求放大到数据库/缓存层,变成 DoS 放大器。
|
|
22
|
-
|
|
23
|
-
需要 readiness 语义的业务(如 k8s readinessProbe)应在业务项目里自行实现,并把该端点限制在内网或挂鉴权。
|
|
24
|
-
|
|
25
|
-
## create_health_router 参数
|
|
26
|
-
|
|
27
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
28
|
-
|------|------|--------|------|
|
|
29
|
-
| service_name | str | **必填** | 服务名,通常取自 `Settings.service_name` |
|
|
30
|
-
| version | str | **必填** | 版本号,通常取自 `__version__` |
|
|
31
|
-
| path | str | `"/health"` | 端点路径 |
|
|
32
|
-
| include_in_schema | bool | True | 是否在 OpenAPI 文档暴露 |
|
|
33
|
-
|
|
34
|
-
## 响应示例
|
|
35
|
-
|
|
36
|
-
```json
|
|
37
|
-
{
|
|
38
|
-
"code": 200,
|
|
39
|
-
"message": "success",
|
|
40
|
-
"data": {
|
|
41
|
-
"service": "my-api",
|
|
42
|
-
"version": "1.0.0",
|
|
43
|
-
"status": "alive"
|
|
44
|
-
},
|
|
45
|
-
"timestamp": 1775625000
|
|
46
|
-
}
|
|
47
|
-
```
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# logger — 基于 loguru 的全局日志
|
|
2
|
-
|
|
3
|
-
> 何时读这份:当用户需要配置日志输出、轮转策略、拦截 uvicorn/SQLAlchemy 日志、或使用 JSON Lines 格式对接日志聚合系统时读取。
|
|
4
|
-
|
|
5
|
-
```python
|
|
6
|
-
from dwyeapi import logger
|
|
7
|
-
|
|
8
|
-
# 启动时调用一次(通常在 lifespan 中)
|
|
9
|
-
logger.configure(
|
|
10
|
-
level="INFO",
|
|
11
|
-
log_dir="./logs",
|
|
12
|
-
filename="app",
|
|
13
|
-
max_bytes=100 * 1024 * 1024, # 100 MB
|
|
14
|
-
retention="30 days",
|
|
15
|
-
console=True,
|
|
16
|
-
serialize=False,
|
|
17
|
-
enqueue=True,
|
|
18
|
-
intercept_stdlib=True,
|
|
19
|
-
intercept_loggers=["uvicorn", "sqlalchemy"],
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
# 获取命名 logger
|
|
23
|
-
log = logger.get_logger("users")
|
|
24
|
-
log.info("用户 %s 登录", user_id)
|
|
25
|
-
log.error("操作失败", exc_info=True)
|
|
26
|
-
log.exception("未捕获异常") # 自动 exc_info=True
|
|
27
|
-
|
|
28
|
-
# 关闭(lifespan 结束时)
|
|
29
|
-
logger.close()
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## configure 参数
|
|
33
|
-
|
|
34
|
-
| 参数 | 类型 | 默认值 | 说明 |
|
|
35
|
-
|------|------|--------|------|
|
|
36
|
-
| level | str | `"INFO"` | 最低级别:DEBUG/INFO/WARNING/ERROR/CRITICAL |
|
|
37
|
-
| log_dir | str/Path/None | None | 日志文件目录,None 表示关闭文件输出 |
|
|
38
|
-
| filename | str | `"app"` | 文件基础名,最终为 `{filename}_YYYY-MM-DD.log` |
|
|
39
|
-
| max_bytes | int | `100*1024*1024` | 单文件大小上限(字节) |
|
|
40
|
-
| retention | str/int | `"30 days"` | 保留策略(如 `"1 week"`、整数个数) |
|
|
41
|
-
| console | bool | True | 是否输出彩色日志到 stderr |
|
|
42
|
-
| console_format | str/None | None | 自定义控制台格式 |
|
|
43
|
-
| file_format | str/None | None | 自定义文件格式 |
|
|
44
|
-
| serialize | bool | False | 文件 sink 是否按 JSON Lines 序列化 |
|
|
45
|
-
| enqueue | bool | True | 是否异步写入(非阻塞、多进程安全) |
|
|
46
|
-
| intercept_stdlib | bool | True | 是否拦截 stdlib `logging` |
|
|
47
|
-
| intercept_loggers | list[str]/None | None | 限定拦截的 logger 名前缀,None 表示拦截 root |
|
|
48
|
-
|
|
49
|
-
## Logger 门面 API
|
|
50
|
-
|
|
51
|
-
`get_logger()` 返回的 `Logger` 兼容 stdlib 语义:
|
|
52
|
-
|
|
53
|
-
- `debug(msg, *args, exc_info=False)`
|
|
54
|
-
- `info(msg, *args, exc_info=False)`
|
|
55
|
-
- `warning(msg, *args, exc_info=False)`
|
|
56
|
-
- `error(msg, *args, exc_info=False)`
|
|
57
|
-
- `exception(msg, *args)` — 等价于 `error(msg, *args, exc_info=True)`
|
|
58
|
-
- `critical(msg, *args, exc_info=False)`
|
|
59
|
-
|
|
60
|
-
所有方法支持 `%s` / `%d` 等 stdlib 占位符,空 args 时保留 msg 原样。
|