补齐相关日志日志

This commit is contained in:
2026-09-16 00:23:49 +08:00
parent 8c2ea84939
commit b2e5eef3c8
39 changed files with 1576 additions and 176 deletions
@@ -0,0 +1,173 @@
---
name: 日志问题全量修复
overview: 依据 docs/日志专项评估报告-2026-09-15.md,以"纯日志改进,零控制流变更"为边界,全量修复 H1-H10 / M1-M8 / L1-L5 共 23 项问题及覆盖矩阵点名的具体静默点:补齐缺失日志、修正级别与字段、修复 trace 断链、增加任务心跳与 Debug 能力、改进日志配置健壮性,并通过构建/测试与逐项 diff 复核保证行为不变。
todos:
- id: fix-trade-logging
content: 补齐交易与回调链路日志:H1-H7/H9 静默点、成功审计与去重,不改控制流
status: completed
- id: fix-auth-user-logging
content: 补齐鉴权与用户链路日志:H8 拒绝分支、登录注册与账号操作静默点
status: completed
- id: fix-clothes-community-logging
content: 补齐衣物与社区链路日志:上传、删除、公开、双写等静默点
status: completed
- id: fix-ai-horoscope-logging
content: 补齐 AI 推荐与运势链路日志:提交、落库、降级静默点
status: completed
- id: fix-quota-scheduler-logging
content: 补齐额度与调度日志:H10、trace 断链与心跳,用 [skill:lsp-code-analysis] 核查签名引用
status: completed
- id: fix-log-infra
content: 改造日志基础设施:访问日志分级、Recovery 结构化、LogLevel 容错与 fanout trace
status: completed
- id: verify-and-annotate
content: 用 [subagent:code-explorer] 复核修复完整性,构建测试通过后标注评估报告状态
status: completed
dependencies:
- fix-trade-logging
- fix-auth-user-logging
- fix-clothes-community-logging
- fix-ai-horoscope-logging
- fix-quota-scheduler-logging
- fix-log-infra
---
## Product Overview
依据《日志专项评估报告(2026-09-15)》对后端服务实施全量日志修复:覆盖报告列出的 23 项编号问题(高 10 项、中 8 项、低 5 项)及评估矩阵点名的全部日志静默点,涉及 20 余个源文件。修复完成后,关键业务失败场景均可在日志中定位、日志可关联同一次请求、级别与字段规范统一,并在评估报告中逐项标注修复状态。全部改动限定为纯日志改进:不改变任何业务行为。
## Core Features
- 交易与回调链路:补齐回调验证、幂等锁、支付信息落库、退款流转、过期补发货、下单失败等静默场景日志,并补成功侧审计日志
- 鉴权与用户链路:补齐各类鉴权拒绝分支、令牌刷新、登录注册、账号操作失败的日志
- 衣物与社区链路:补齐上传校验、删除回退、公开扣额、双写一致性等失败日志
- AI 推荐与运势链路:补齐任务提交、后台生成、落库、降级场景日志
- 额度与后台任务:补齐额度事务失败日志,六个定时任务增加每轮运行记录
- 日志基础设施:访问日志按状态分级、异常恢复输出纳入统一日志、配置容错、链路标识完整注入
- 评估报告逐项标注修复状态(已修复 + 日期)
## Tech Stack
- 语言与框架:Go 1.27 + Gin(沿用现状,不新增任何第三方依赖)
- 日志:标准库 log/slog(JSON 结构化)+ lumberjack 滚动文件(沿用既有输出通道与 OpenTelemetry 桥接)
- 验证工具:gofmt、go vet、go build、go test
## Implementation Approach
### 总体策略
按"文件集互不重叠"的 6 个修复批次顺序执行,加 1 个验证与标注批次(共 7 个任务):
| 批次 | 文件集 | 覆盖问题 |
| --- | --- | --- |
| T1 交易与回调 | order.go、virtualpay.go、virtualpay_compensate.go、benefit_compensation.go、order_expire.go、routers/api/v1/order.go | H1-H7、H9、M1/M2/M3/M4/M5、L3 |
| T2 鉴权与用户 | lzcwyjwt.go、user.go | H8、M1/M2/M3、L4、矩阵缺口 |
| T3 衣物与社区 | clothes.go、outfit.go、like.go、collect.go、closet.go、outfit_share.go、notification.go | H10(clothes 部分)、M2/M3/M4/M5、L4/L5、矩阵缺口 |
| T4 AI 与运势 | airecommend.go、horoscope.go、ai_outfit_cleanup.go | M2/M3/M4/M5、L5、矩阵缺口 |
| T5 额度与调度 | service.go、quota.go、quota_expire.go、tokenhub/scheduler.go、redismodel/keys.go | H10(service 部分)、M3/M4/M5、L4 |
| T6 基础设施 | middleware/logger.go、middleware/recovery.go(新增)、routers/router.go、main.go | M6、L1、L2、M3(fanout) |
| T7 验证与标注 | 全仓复核 + 评估报告状态标注 | 全部 |
### 关键设计决策
1. **唯一落点去重**:每个失败场景只在有完整业务上下文处记录一次。普通支付/退款回调的失败判定(验签/解密/非 SUCCESS/金额等)统一由 service 层记录,handler 只回 FAIL 应答不重复记录;XPay 入口层失败(读体/验签/解密/兼容回显/加密降级)service 不可见,由 handler 层记录;pkg/wechat 等客户端不加日志(错误上抛到 service 记录),避免与 H1 重复。
2. **静默值统一范式**:被 `_ =` 丢弃的返回值改为"接收并记录错误但不阻断后续流程":order.go:461-465、virtualpay.go:426-430、virtualpay.go:370(仅记录,仍 return nil)、outfit.go:277-281、closet.go:131-141 等。
3. **锁错误拆分**:`lerr != nil` 记 Error、`!ok`(正常竞争)记 Debug;分支行为不变(仍 return nil 当作并发处理)。
4. **级别规范**:业务性拒绝 Info;可恢复 / best-effort 失败 Warn;主流程与资损失败 Error;高频与细节路径 Debug(补齐全仓 Debug 能力,L5)。
5. **链路标识**:ctx 可得时一律使用 slog.XxxContext;service.go 事务函数(cosRemainTx、refundCosQuotaTx)透传 ctx;clothes.go:320 清理 context 改用 context.WithoutCancel(ctx) 派生以保留 span 值;user.go:541 异步复位派生独立 span;main.go 将 OtelContextHandler 移至 FanoutHandler 外层,使本地与 OTel 两条分支都注入 trace_id/span_id(本地输出等价、OTel 分支增强)。
6. **后台任务心跳**:每轮有产出记 Info、无产出 / 零值轮次记 Debug(高频任务保持低噪),字段含 scanned / affected / failed 等。
7. **访问日志与 Recovery**:访问日志按状态码分级(5xx 记 Error、4xx(除 401)记 Warn、401 与其余记 Info),user_agent 截断 256 字符;新增 Recovery 中间件:panic 信息与堆栈经 slog.ErrorContext 输出,HTTP 500 与 broken-pipe 处理保持 gin 默认语义。
8. **配置健壮性**:LogLevel 忽略大小写匹配,未匹配值在 logger 就绪后告警并回落 Info;lumberjack 增加 LocalTime: true;AddSource 保留现状(按级别裁剪需自定义 Handler,复杂度与收益不成比例,在报告中注明理由)。
### 性能与可靠性
- 新增日志均为单点 O(1) 调用,无循环内逐条日志;批量任务使用每轮汇总;高频路径(轮询、懒过期、零值轮次)使用 Debug 级,生产默认 Info 下零输出开销;
- 日志调用不进入任何事务 / 锁语义,不改变延迟与并发行为;
- 既有良好实践不回退:资损防线 Error 打点、字段命名(order_no / out_trade_no / _amount)、trace 关联率、敏感信息零泄漏(query 脱敏)。
## Implementation Notes
- 行号基准:以当前工作区文件为准(存在未提交改动),执行时逐文件核对后再修改,不得套用历史报告行号;
- 敏感值红线:不打印 openid 值、session_key、token、回调报文正文(回调报文仅入库);引用字段名不受限;
- 去重检查:同一失败场景唯一落点;同一错误禁止 wrapper 与内层重复记录(virtualpay.go:139/151 需合并为单条);
- msg/字段规范:msg 英文小写、不带标点;字段 snake_case;best-effort 日志不阻断主流程;
- 每批次完成后立即执行 gofmt 与 go build,避免批量错误累积;
- 报告标注:对齐既有 docs 风格(已修复 + 2026-09-15),逐项覆盖 23 项与矩阵缺口。
## Architecture Design
- 无架构变更:不新增分层与模式;唯一新增文件 internal/middleware/recovery.go 属既有 middleware 层;
- 改动分布:service 层 17 个文件(补齐为主);middleware 3 处(鉴权拒绝、访问日志、新增 Recovery);routers 2 处(handler 入口日志、Recovery 替换);main.go 3 处(日志组装、级别容错、LocalTime);pkg 1 处微调(scheduler);
- 数据流不变,仅观测面扩展。
## Directory Structure
```
zt-closet-backend/
├── internal/
│ ├── service/ # 修复主体(17 个文件)
│ │ ├── order.go # [MODIFY] H1(123-129/354-357 回调验签、解密、非 SUCCESS;146/167-170 DB 错误)、H3(134-136/361-363 锁错误拆分)、H4(461-465 记录不阻断)、H5(394-396 settle 失败记录)、H6(196-206 过期补发:成功 Info/DB Error/竞争 Debug)、H7(95-102 落库与 JSAPI 失败)、H9(723-734/786/789 回收静默)、M1(104-115/171-174/341-346/380-389 成功审计)、M2/L3(565/567 补 retry_count/amount/user_id)、M5(438 升 Warn、525 降 Info、741 升 Error)
│ │ ├── virtualpay.go # [MODIFY] H3(244-246/337-339)、H4(426-430)、H5(370 接收返回值记 Error、仍 return nil)、H7(26-63/92-106 业务拒绝 Info、异常 Error)、L3(380 拆分 msg、139/151 去重、345 补 refund_no 与金额)
│ │ ├── virtualpay_compensate.go # [MODIFY] H3(82-85)、M3(142 worker panic 用外层 ctx)、M4(155 每轮汇总补 queried/failed/unpaid)、M5(167 拆分 openid 空/查询失败并修正 error 字段 nil)
│ │ ├── benefit_compensation.go # [MODIFY] H3(43-46)、H9(129-134/61/73/144-146/157 补日志与恢复条数)、L3(154 拆分两种场景)
│ │ ├── order_expire.go # [MODIFY] M4(31-46 每轮汇总:有产出 Info、零值 Debug)
│ │ ├── user.go # [MODIFY] H8(343 吊销失败 Error)、M1(登录成功 Info)、M2/L4(117 补 user_id)、M3(541 独立 span)、矩阵缺口(44-49/70-79/103-105/107-109/111-114/129-131/149-155/222-226/237-294/307-346 各失败点)
│ │ ├── clothes.go # [MODIFY] H10(466-490 删除回退失败)、M2(159 补 user_id、275 retryCall 加业务标识)、M3(320 用 WithoutCancel 派生)、M4(597-620 每轮汇总)、L4(320 补 phase)、L5(143/189/197 状态推进 Debug)、矩阵缺口(55-82 上传校验、84-87 扣额、92-96 落盘、104-109 建记录、609-617 认领与损坏)
│ │ ├── outfit.go # [MODIFY] 矩阵缺口(69-83/136-156/217-224 事务失败 Error、245-248 锁竞争 Debug 与解锁 Warn、256-281 公开失败与扣额回退)、M2(289 补 outfit_uuid)
│ │ ├── like.go / collect.go # [MODIFY] 双写失败(like:60/73、collect:71/78)Warn、写路径 DB 失败 Error、降级 Debug
│ │ ├── closet.go # [MODIFY] 矩阵缺口(72-74/83-94/131-134/139-141/302-304)、M2(102 补 share_uuid)
│ │ ├── outfit_share.go # [MODIFY] 矩阵缺口(63-65/74-80/218-220)
│ │ ├── notification.go # [MODIFY] 矩阵缺口(95-97 Error、124-131 双失败)、M5(170 降 Warn)
│ │ ├── airecommend.go # [MODIFY] 矩阵缺口(68-103 提交三失败点、120-127、153-175、254-260 空衣橱 Info、294-306 落库失败、364-386 运势降级)、M2(270 补 task_id)、M3(417 传 ctx)、M5(213 降 Warn)、L5(轮询 Debug)
│ │ ├── horoscope.go # [MODIFY] 矩阵缺口(51-56/59-67/72-74)
│ │ ├── ai_outfit_cleanup.go # [MODIFY] M4(64-81 每轮汇总)
│ │ ├── service.go # [MODIFY] H10(350-355/103-109/141-144/155-157)、M3(375/434 事务函数透传 ctx)、L4(461 补 related_id/reason)
│ │ └── quota.go / quota_expire.go # [MODIFY] 矩阵缺口(quota:37-40/59-62);M4(quota_expire:80 汇总)
│ ├── middleware/
│ │ ├── lzcwyjwt.go # [MODIFY] H8(30-34/36-41/44-56/59-65 各分支 Warn、71-75 UID 不匹配 Error、84-89/95-99 Warn、91 回填失败 Debug);52 行响应文案不动
│ │ ├── logger.go # [MODIFY] M6(44-53 按状态分级、user_agent 截断)
│ │ └── recovery.go # [NEW] M6:Recovery() 中间件,panic 与堆栈经 slog 输出,500 与 broken-pipe 语义保持 gin 默认
│ ├── routers/
│ │ ├── router.go # [MODIFY] M6:gin.Recovery() 替换为 middleware.Recovery()
│ │ └── api/v1/order.go # [MODIFY] H2(180/187-192/152-173/211-214);普通回调 133-137/142-143 不重复记录(service 唯一落点)
│ └── redismodel/keys.go # [MODIFY] M3(39 启动期无 span,保留并加注释理由)
├── pkg/tokenhub/scheduler.go # [MODIFY] M3/M5(115 用 ctx 并拆分 lerr 记 Error、!ok 记 Info;118 解锁失败 Warn)
├── main.go # [MODIFY] L1(LogLevel 忽略大小写与未匹配告警)、L2(LocalTime: true;AddSource 保留并注理由)、M3(276-291 fanout 重组)
└── docs/日志专项评估报告-2026-09-15.md # [MODIFY] 逐项标注修复状态(已修复 + 2026-09-15)
```
## Key Code Structures
1. main.go 日志组装调整(防误改 fanout.go / otel.go,两者实现均无需改动):
改造前:
```
localHandler := OtelContextHandler{Handler: JSONHandler}
slog.SetDefault(slog.New(NewFanoutHandler(localHandler, otelHandler)))
```
改造后:
```
slog.SetDefault(slog.New(OtelContextHandler{Handler: NewFanoutHandler(JSONHandler, otelHandler)}))
```
本地分支输出等价,OTel 分支获得 trace_id/span_id(FanoutHandler 对子 handler 使用 r.Clone() 分发,外层注入的 attrs 会同时进入两分支)。
2. 新增 Recovery 中间件签名:`internal/middleware/recovery.go` 提供 `func Recovery() gin.HandlerFunc`(基于 gin.CustomRecoveryWithWriter 等价实现,panic 值 + debug.Stack 经 slog.ErrorContext 输出,响应语义与 gin.Recovery 一致)。
## Agent Extensions
### SubAgent
- **code-explorer**
- Purpose: 修复完成后全量复核——重扫全部 slog 打点与静默点,核对 23 项 + 矩阵缺口逐项落地、无重复日志、无敏感输出、ctx 使用正确
- Expected outcome: 输出复核清单与偏差项,支撑评估报告定稿与状态标注
### Skill
- **lsp-code-analysis**
- Purpose: 在签名 / 组装变更点(retryCall 业务标识扩展、service.go 事务函数 ctx 透传、Recovery 替换)执行引用与影响面分析,确保调用点无遗漏、无误改
- Expected outcome: 确认相关符号全部引用点均在预期范围内,签名变更不影响无关调用