| name | logstore |
| description | Wavelet 项目专用:当新增或修改日志/分析用途表(访问日志、审计流水、可观测时序)、接入 internal/repository/logstore、切换日志主库、实现 PG/SQLite 回落,或判断一张表该走业务主库还是日志库时必须使用。 |
日志用途表开发
开始前阅读根目录 AGENTS.md。DDL 用 database-migration;高频写入队列用 clickhouse-batchwriter;切换任务用 new-async-task。本技能只回答:这张表是不是日志表,以及如何接入可切换的日志主库。
分层与切换协议见 日志用途表。
先判定
日志表同时满足:
- 追加写入、几乎不更新单行
- 按时间查询/聚合,允许按保留天数删除
- 关闭 ClickHouse 后仍要能写、能查
- 不参与用户/配置/任务等事务一致性
不要做成日志表:用户、配置、任务执行、上传元数据、需要事务或强一致的业务实体。这些走主库 repository,不要进 logstore。
当前框架已接入的日志表:w_user_access_logs(管理端 API 访问审计)。
分层
| 层级 | 路径 | 职责 |
|---|
| 抽象 | internal/repository/logstore | 接口 + Active/BuildForMigration;apps 只面向这里 |
| CH 实现 | logstore 委托 internal/repository/analytics | 原生 PrepareBatch / ChDB 查询 |
| 主库实现 | logstore GORM | PG(按月分区)与 SQLite(普通表) |
| Model | internal/model/analytics | 实体、TableName、InsertColumns、BatchInsertSQL,无 IO |
| 入队 | internal/apps/<domain> + batchwriter | FlushFunc 调 logstore.Active().….BatchInsert |
| 切换 | internal/apps/admin/logs 的 logs:db_switch | 冻结写入 → 排空 → 复制 → 翻转 log_database |
| 清理 | logstore.CleanupExpired,由 system:cleanup 调用 | 按库读取保留天数后 DeleteBefore |
log_database ∈ {postgres,sqlite,clickhouse},且只能是「随主库」或 ClickHouse:主库为 PG 时日志不能是 SQLite,反之亦然。log_database / log_db_migration 受保护,禁止管理端手动改。
新增一张日志表
按顺序做,列名三库必须一致。
-
Model
在 internal/model/analytics/ 定义 struct;实现 TableName();批量写再提供 InsertColumns() / BatchInsertSQL()。
-
三套 DDL(database-migration)
- ClickHouse:
goose/clickhouse/,MergeTree,PARTITION BY toYYYYMM(时间列)。
- PostgreSQL:
goose/postgres/,高频表用 PARTITION BY RANGE (时间列),复合主键必须包含分区键。
- SQLite:
goose/sqlite/,普通表 + 时间/过滤列索引。
不要在 PG/SQLite 上复制 CH 物化视图;聚合在查询时实时算。
-
logstore 接口
在对应 Store(现有 UserAccessLogStore,或新域自建接口并挂到 Store)补齐至少:
- 写入:
BatchInsert(flush 目标;内调 ensureWritable)
- 查询:业务需要的 List/Count/聚合
- 迁移:
ListForMigration(afterID, limit)、MigrationRange、DeleteAll、EnsurePartitions(PG 按月预建,CH/SQLite no-op)
- 清理:
DeleteBefore(cutoff)
-
双实现
- CH:委托
analyticsrepo,零额外查询路径。
- GORM:PG/SQLite 共用一套;方言 SQL 只放小函数(如按日
to_char / strftime)。零值 id 落库前用 idgen.NextUint64ID()。
-
buildStore
在 provider.go 的 CH / GORM 分支同时挂上新域。
-
写入
apps 用独立 batchwriter 实例;FlushFunc → logstore.Active(ctx) → BatchInsert。禁止 analyticsrepo.BatchInsert、禁止 db.ChConn。迁移任务调用域的 Drain(等队列空一个 flush 周期,不要 Stop writer)。
-
切换任务
在 copy* 流程增加该表:DeleteAll 目标 → MigrationRange + EnsurePartitions → 按 id 分页复制。不要改切换协议(仍冻结写入、源数据不删、成功才翻转)。
-
对该表 ;保留天数用已有 ,不要为单表再发明一套 key,除非产品明确要求独立 TTL。
禁止
- apps 直接
import internal/repository/analytics 或 db.ChConn / db.ChDB 做日志读写
- 只建 CH 表、不建 PG/SQLite 回落
- 在 Handler 里逐条
PrepareBatch + Send
- 把业务表「顺便」放进 logstore 以便关 CH
- 管理端 API 改
log_database / log_db_migration
验证
go test ./internal/repository/logstore ./internal/repository/analytics
go test ./internal/apps/admin/logs ./internal/apps/risk_control ./internal/platform/bootstrap
make swagger
make code-check
对照:w_user_access_logs 的 model、三库 goose、logstore GORM/CH、risk_control.InitLogWriter、logs.LogDBSwitchHandler、system:cleanup。