114 lines
6.5 KiB
Markdown
114 lines
6.5 KiB
Markdown
---
|
||
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` 定义错误的**类型**(Code),Log 记录错误的**现场**(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 一个文件),还是按 **时间** 切割(每天一个文件)?
|
||
- _通常建议_: 按大小切割(防止单文件过大无法打开),配合最大文件保留数。
|