Files
2025-12-11 07:24:36 +08:00

114 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
tags: []
aliases:
- 1. 核心设计目标 (Core Design Goals)
date created: 星期三, 十二月 10日 2025, 10:27:39 晚上
date modified: 星期三, 十二月 10日 2025, 10:28:15 晚上
---
# 1. 核心设计目标 (Core Design Goals)
## 目标一:全链路上下文关联 (Contextual Traceability)
这是最核心的差异点。传统的 `log.Println("Database error")` 在并发环境下毫无价值,因为你不知道这条错误属于哪个请求。
- **设计要求**:
- **自动注入 TraceID**: 必须能够从 `context.Context` 中提取 `TraceID`(目前 `internal/pkg/app` 已经生成了 TraceID并自动将其附加到每一条日志中。
- **请求元数据绑定**: 除了 TraceID还应支持自动绑定 `UserID``IP``Method``Path` 等元数据,形成请求的完整快照。
- **跨组件穿透**: 日志对象必须能够在 Layer 之间传递(例如 Controller -> Service -> Repository且保持上下文不丢失。
## 目标二:严格的结构化契约 (Strict Structured Schema)
日志是写给机器看的,不是写给通过 SSH 连上服务器的人看的。
- **设计要求**:
- **JSON First**: 生产环境强制使用 JSON 格式。
- **Schema 统一**: 字段命名必须统一。例如,不要混用 `uid`, `user_id`, `userId`,必须在设计阶段锁定为 snake_case (如 `user_id`)。
- **类型安全**: 时间戳必须统一格式(推荐 ISO8601 或 Unix Nano数字字段不能变成字符串便于聚合计算
## 目标三:高性能与零侵入 (High Performance & Zero Allocation)
日志通常是系统中 IO 最密集的组件之一。
- **设计要求**:
- **低 GC 压力**: 利用 Zap 的核心优势,避免大量的 `interface{}` 反射和字符串拼接,使用强类型的 Field`zap.Int`, `zap.String`)。
- **异步 IO (可选)**: 考虑是否引入 Buffer 机制(牺牲极端崩溃下的日志完整性换取吞吐量)。
- **Level 级联过滤**: 在 Debug 级别关闭时Debug 级别的日志构造逻辑(如复杂的对象序列化)不应被执行。
## 目标四:安全与合规 (Security & Compliance)
这往往是被忽视的一点,也是导致安全事故的频发区。
- **设计要求**:
- **敏感数据脱敏**: 必须具备“黑名单”机制。任何包含 `password`, `token`, `mobile`, `credit_card` 的字段在输出前必须被自动掩盖Masking
- **安全截断**: 防止打印过大的 Body如 Base64 图片上传)导致磁盘爆满或日志系统瘫痪,限制单条日志最大长度。
---
# 2. 场景化行为对比 (Dev Vs Prod)
为了兼顾开发体验和生产运维标准,我们需要在设计中明确区分两种环境的行为。
|**维度**|**开发环境 (Development)**|**生产环境 (Production)**|**设计意图**|
|---|---|---|---|
|**编码格式**|Console (彩色,人类易读)|JSON (机器易读)|开发追求直观;生产追求 ELK 解析效率。|
|**输出目标**|Stdout (控制台)|File + Stdout (双写)|开发侧容器即焚;生产侧需持久化 + 容器采集。|
|**日志级别**|Debug|Info / Warn|生产环境过滤掉大量 Debug 噪音,节省存储成本。|
|**堆栈追踪**|Error 级别即打印|Panic 或 Fatal 才打印|减少生产环境日志体积,除非发生严重故障。|
|**调用行号**|显示 (Caller)|显示 (Caller)|快速定位代码位置。|
---
# 3. 架构定位与边界 (Architecture Boundary)
我们需要明确日志模块在架构中的位置:
- **位置**: 属于 `Infrastructure Layer` (Level 0/1)。
- **依赖关系**:
- **被谁依赖**: 所有层Handler, Service, Repository都依赖 Log。
- **依赖谁**: 仅依赖标准库和第三方 Log Driver (Zap)**不应依赖业务逻辑**。
- **与其他模块的关系**:
- **vs `ecode`**: `ecode` 定义错误的**类型**CodeLog 记录错误的**现场**Stack/Trace
- **vs `app.Response`**: Response 负责**对用户说话**经过清洗的、友好的信息Log 负责**对开发者说话**(原始的、包含脏数据的真相)。
---
# 4. 深度反思与自我反驳 (Critical Thinking & Risk Analysis)
在敲定设计目标前,必须审视潜在的矛盾和风险:
**反驳点 1全链路上下文TraceID的传递成本**
- **挑战**: 要想让 Repository 层的日志也打出 TraceID必须修改所有方法的签名为 `func (ctx context.Context, …)`。这对现有代码(如果是非 Context 风格)是巨大的重构。
- **回应**: 我们的 `Repository` 接口目前设计中已经包含了 `context.Context`。这是一个必须遵守的“硬约束”。如果缺少 Context日志将断层。
- **结论**: 必须在规范中强调:**所有层级的方法首个参数必须是 Context**。
**反驳点 2脱敏机制的性能损耗**
- **挑战**: 如果每一条日志都要遍历字段去匹配“黑名单”进行正则替换CPU 开销极大。
- **回应**: 不能使用正则扫描全文。
- **修正方案**: 利用 Zap 的 `Hook``Core` 包装,仅针对特定 Key`password`)进行值替换,或者要求开发者在打印敏感结构体时显式调用 `.Reduct()` 方法,而非隐式全局扫描。隐式扫描在 Golang 中通常是性能杀手。
**反驳点 3异步写入的丢数据风险**
- **挑战**: 为了性能使用 `Buffered Write`,如果进程被 `kill -9` 或 Panic 崩溃,缓冲区日志会丢失,而崩溃前的日志往往最重要。
- **回应**: 对于交易类系统,**可靠性 > 吞吐量**。
- **结论**: 默认采用 **同步写入 (Sync Write)**。Zap 本身性能已经足够强(纳秒级),除非达到数万 QPS否则不需要引入 Buffer。对于 Panic必须使用 `defer logger.Sync()` 确保刷盘。
---
# 5. 待确认问题 (Open Questions)
在进入下一步(编写详细规格说明书)之前,我需要确认你对以下几点的偏好:
1. **全局单例 vs 纯依赖注入**:
- _选项 A_: 提供 `log.Info()` 全局静态方法(方便,但有副作用)。
- _选项 B_: 强制必须通过 `l.Info()` 实例方法调用(架构更洁癖,但调用繁琐)。
- _推荐_: **选项 A + B**。提供全局方法作为快捷方式(底层代理到单例),同时支持 DI 注入。你倾向于哪种?
2. **日志轮转 (Rotation) 策略**:
- 你是倾向于按 **大小** 切割(如 100MB 一个文件),还是按 **时间** 切割(每天一个文件)?
- _通常建议_: 按大小切割(防止单文件过大无法打开),配合最大文件保留数。