15 KiB
15 KiB
日志配置
**本文引用的文件** - [cmd/server/main.go](file://cmd/server/main.go) - [pkg/config/config.go](file://pkg/config/config.go) - [pkg/logger/logger.go](file://pkg/logger/logger.go) - [pkg/logger/manager.go](file://pkg/logger/manager.go) - [internal/middleware/logger.go](file://internal/middleware/logger.go) - [internal/middleware/recovery.go](file://internal/middleware/recovery.go) - [go.mod](file://go.mod)目录
简介
本文件面向 CarrotSkin 项目的开发者与运维人员,系统性说明日志系统的配置与使用方式。重点围绕以下配置项展开:log.level、format、output、max_size、max_backups、max_age 和 compress;并结合 LogConfig 结构体与实际代码实现,给出不同日志级别选择策略、JSON 格式与文本格式的适用场景、以及基于大小与天数的日志轮转工作机制。最后提供生产环境监控集成建议与最佳实践。
项目结构
日志系统由“配置加载层”“日志工厂层”“全局管理器层”“中间件与业务使用层”四部分组成:
- 配置加载层:从环境变量加载并解析为 Config,其中包含 LogConfig 字段。
- 日志工厂层:根据 LogConfig 构建 zap.Logger。
- 全局管理器层:提供 Init、GetLogger、MustGetLogger 等接口,确保单例与线程安全。
- 中间件与业务使用层:通过中间件注入日志实例,统一记录 HTTP 请求与异常恢复。
graph TB
A["配置加载<br/>pkg/config/config.go"] --> B["日志工厂<br/>pkg/logger/logger.go"]
B --> C["全局管理器<br/>pkg/logger/manager.go"]
C --> D["中间件使用<br/>internal/middleware/logger.go"]
C --> E["恢复中间件<br/>internal/middleware/recovery.go"]
C --> F["应用入口初始化<br/>cmd/server/main.go"]
图表来源
- pkg/config/config.go
- pkg/logger/logger.go
- pkg/logger/manager.go
- internal/middleware/logger.go
- internal/middleware/recovery.go
- cmd/server/main.go
章节来源
核心组件
- LogConfig 结构体:定义日志系统的核心配置项,来源于环境变量映射。
- New:根据 LogConfig 构建 zap.Logger,支持 JSON/Console 编码器、stdout 或文件输出。
- Init/GetLogger/MustGetLogger:全局单例管理,保证线程安全与幂等初始化。
- 中间件 Logger/Recovery:在请求处理前后记录日志,并在 panic 时记录堆栈与上下文。
章节来源
- pkg/config/config.go
- pkg/logger/logger.go
- pkg/logger/manager.go
- internal/middleware/logger.go
- internal/middleware/recovery.go
架构总览
下图展示了从配置到日志输出的关键流程:应用启动时加载配置,初始化日志管理器,随后中间件与业务代码通过全局日志实例进行记录。
sequenceDiagram
participant App as "应用入口<br/>cmd/server/main.go"
participant Cfg as "配置加载<br/>pkg/config/config.go"
participant LMgr as "日志管理器<br/>pkg/logger/manager.go"
participant LNew as "日志工厂<br/>pkg/logger/logger.go"
participant MW as "中间件<br/>internal/middleware/logger.go"
participant Rec as "恢复中间件<br/>internal/middleware/recovery.go"
App->>Cfg : 加载配置
Cfg-->>App : 返回 Config(LogConfig)
App->>LMgr : Init(LogConfig)
LMgr->>LNew : New(LogConfig)
LNew-->>LMgr : 返回 zap.Logger
LMgr-->>App : 初始化完成
App->>MW : 注入日志实例
App->>Rec : 注入日志实例
MW-->>App : 记录请求日志
Rec-->>App : 记录 panic 与堆栈
图表来源
- cmd/server/main.go
- pkg/config/config.go
- pkg/logger/manager.go
- pkg/logger/logger.go
- internal/middleware/logger.go
- internal/middleware/recovery.go
详细组件分析
配置项详解与选择策略
- log.level
- 取值范围:debug、info、warn、error,默认 info。
- 选择策略:
- 开发/调试:debug,便于定位问题。
- 生产:info,平衡可观测性与性能;仅在需要深入排查时临时提升到 debug。
- 严重问题:error,聚焦错误事件,避免过多噪声。
- log.format
- 取值:console、json。
- 适用场景:
- console:本地开发终端阅读友好,便于快速定位。
- json:便于日志收集与结构化分析,适合生产与集中化监控。
- log.output
- 取值:空/“stdout”表示输出到标准输出;其他路径将自动创建目录并写入文件。
- 适用场景:
- 容器化部署:stdout,配合容器日志采集。
- 传统部署:文件路径,结合日志轮转策略。
- log.max_size、log.max_backups、log.max_age、log.compress
- 作用:控制日志轮转策略(按大小与天数)与压缩开关。
- 工作机制(基于大小与天数):
- 当单个日志文件达到 max_size(MB)时触发轮转。
- 最多保留 max_backups 个历史备份文件。
- 历史文件保留不超过 max_age(天)。
- compress=true 时对旧文件进行压缩,节省磁盘空间。
- 注意:当前代码实现中并未直接使用上述字段进行轮转配置,而是采用文件输出与基础写入同步。若需启用基于大小/天数的轮转,可在日志工厂中引入外部轮转库(如 lumberjack)并传入上述参数。
章节来源
日志级别选择策略
- debug:用于开发阶段的详细追踪,包含大量上下文信息。
- info:生产环境默认级别,记录关键业务事件与系统状态。
- warn:潜在问题或异常流程,需关注但不影响整体运行。
- error:错误事件,必须处理与上报。
章节来源
JSON 格式与文本格式的适用场景
- JSON:
- 优点:结构化强,便于日志聚合、检索与告警。
- 适用:生产环境、集中化日志平台(如 ELK、Loki、Cloud Logging)。
- Console:
- 优点:人类可读性强,适合本地开发与快速排障。
- 适用:本地调试、临时诊断。
章节来源
日志轮转策略(基于大小与天数)
- 当前实现要点:
- 输出到文件时会自动创建目录并追加写入。
- 未显式配置基于大小/天数的轮转。
- 建议扩展方案:
- 引入外部轮转库(如 lumberjack),在 New 中根据 max_size、max_backups、max_age、compress 进行轮转配置。
- 将轮转配置作为可选参数传入,保持现有接口兼容。
章节来源
日志工厂与全局管理器
- New:根据 LogConfig 构建 zap.Logger,设置级别、编码器与输出目标。
- Init/GetLogger/MustGetLogger:提供线程安全的单例初始化与获取能力,避免重复初始化。
classDiagram
class LogConfig {
+string level
+string format
+string output
+int max_size
+int max_backups
+int max_age
+bool compress
}
class LoggerFactory {
+New(cfg LogConfig) *zap.Logger
}
class LogManager {
+Init(cfg LogConfig) error
+GetLogger() (*zap.Logger, error)
+MustGetLogger() *zap.Logger
}
LogConfig --> LoggerFactory : "输入"
LoggerFactory --> LogManager : "被调用"
图表来源
章节来源
中间件与业务使用
- Logger 中间件:记录每次 HTTP 请求的方法、路径、状态码、耗时、客户端 IP、User-Agent 等。
- Recovery 中间件:捕获 panic 并记录错误、路径、方法、IP 与完整堆栈,返回统一错误响应。
sequenceDiagram
participant Client as "客户端"
participant Router as "Gin 路由"
participant MW as "日志中间件"
participant Handler as "业务处理器"
participant Rec as "恢复中间件"
Client->>Router : 发起请求
Router->>MW : 进入日志中间件
MW->>MW : 记录请求开始时间与上下文
MW->>Handler : 继续处理
Handler-->>MW : 返回响应
MW->>MW : 记录状态码、耗时、IP、UA
MW-->>Router : 返回响应
Note over MW : 若发生 panic,由恢复中间件接管
Router->>Rec : 进入恢复中间件
Rec->>Rec : 记录错误与堆栈
Rec-->>Client : 返回 500 错误
图表来源
章节来源
依赖关系分析
- 外部依赖:zap 用于高性能日志记录;viper 用于配置加载;godotenv 用于 .env 支持。
- 内部依赖:配置模块提供 LogConfig;日志模块提供 Init/GetLogger;中间件依赖日志实例。
graph LR
subgraph "外部依赖"
Z["go.uber.org/zap"]
V["github.com/spf13/viper"]
D["github.com/joho/godotenv"]
end
subgraph "内部模块"
CFG["pkg/config/config.go"]
LOGF["pkg/logger/logger.go"]
LOGM["pkg/logger/manager.go"]
MWL["internal/middleware/logger.go"]
MWR["internal/middleware/recovery.go"]
MAIN["cmd/server/main.go"]
end
V --> CFG
D --> CFG
CFG --> LOGM
LOGM --> LOGF
MAIN --> LOGM
MAIN --> MWL
MAIN --> MWR
Z --> LOGF
Z --> MWL
Z --> MWR
图表来源
- go.mod
- pkg/config/config.go
- pkg/logger/logger.go
- pkg/logger/manager.go
- internal/middleware/logger.go
- internal/middleware/recovery.go
- cmd/server/main.go
章节来源
- go.mod
- pkg/config/config.go
- pkg/logger/logger.go
- pkg/logger/manager.go
- internal/middleware/logger.go
- internal/middleware/recovery.go
- cmd/server/main.go
性能考量
- 使用 zap:具备高性能与零分配特性,适合高并发场景。
- 选择合适的日志级别:生产环境默认 info,避免 debug 的额外开销。
- 输出目标选择:stdout 便于容器日志采集;文件输出需注意磁盘 IO 与轮转策略。
- 压缩与保留策略:合理设置 max_size、max_backups、max_age 与 compress,平衡磁盘占用与检索效率。
故障排查指南
- 未初始化日志即使用:
- 现象:调用 GetLogger 返回错误提示“日志未初始化,请先调用 logger.Init()”。
- 排查:确认应用入口是否在启动时调用了 logger.Init(cfg.Log)。
- 输出到文件失败:
- 现象:初始化日志时报错,无法打开文件。
- 排查:检查 log.output 指定路径是否存在写权限,目录是否可创建。
- panic 未记录:
- 现象:服务崩溃但无日志记录。
- 排查:确认已注册 Recovery 中间件并注入了日志实例。
- 日志轮转未生效:
- 现象:日志文件持续增大。
- 排查:当前实现未使用 max_size/max_backups/max_age/compress 进行轮转,需扩展轮转逻辑。
章节来源
结论
- CarrotSkin 的日志系统以 zap 为核心,通过配置驱动实现灵活的日志级别、编码器与输出目标切换。
- 当前未直接启用基于大小/天数的轮转,建议在日志工厂中引入轮转库并使用 LogConfig 的 max_size、max_backups、max_age、compress 字段。
- 在生产环境中推荐使用 JSON 格式与 stdout 输出,结合集中化日志平台与告警策略,提升可观测性与可维护性。
附录
配置项一览与默认值
- log.level:默认 info
- log.format:默认 json
- log.output:默认 logs/app.log(文件输出)
- log.max_size:默认 100 MB
- log.max_backups:默认 3
- log.max_age:默认 28 天
- log.compress:默认 true
章节来源
环境变量映射
- LOG_LEVEL → log.level
- LOG_FORMAT → log.format
- LOG_OUTPUT → log.output
章节来源
配置示例与最佳实践
- 示例一:本地开发(控制台输出,便于阅读)
- LOG_LEVEL=debug
- LOG_FORMAT=console
- LOG_OUTPUT=stdout
- 示例二:生产(结构化日志,集中化采集)
- LOG_LEVEL=info
- LOG_FORMAT=json
- LOG_OUTPUT=stdout
- 示例三:传统服务器(文件输出,配合轮转)
- LOG_LEVEL=info
- LOG_FORMAT=json
- LOG_OUTPUT=/var/log/carrotskin/app.log
- max_size=100
- max_backups=3
- max_age=28
- compress=true
- 最佳实践:
- 生产环境统一使用 JSON 格式与 stdout,便于容器日志采集。
- 严格控制日志级别,避免 debug 在生产长期开启。
- 对关键路径增加结构化字段(如用户 ID、请求 ID),便于关联追踪。
- 在中间件中统一记录请求上下文,确保异常时具备足够信息。
- 如需文件轮转,建议在日志工厂中引入轮转库并使用上述配置项。
章节来源