Files
tyapi-server/docs/Zap官方最佳实践日志系统指南.md
2025-08-25 15:44:06 +08:00

454 lines
11 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.

# 🚀 Zap 官方最佳实践日志系统指南
## 概述
本日志系统完全基于 [Zap 官方最佳实践](https://betterstack.com/community/guides/logging/go/zap/) 设计,使用 `zap.NewProduction()``zap.NewDevelopment()` 预设,提供高性能、结构化的日志记录。
## ✨ 核心特性
### 1. **基于 Zap 官方预设**
- 使用 `zap.NewProduction()` 生产环境预设
- 使用 `zap.NewDevelopment()` 开发环境预设
- 自动添加调用者信息、堆栈跟踪等
### 2. **全局日志器支持**
- 支持 `zap.ReplaceGlobals()` 全局替换
- 提供 `logger.L()``logger.GetGlobalLogger()` 访问
- 符合 Zap 官方推荐的使用方式
### 3. **强类型字段支持**
- 使用 `zap.String()`, `zap.Int()`, `zap.Error()` 等强类型字段
- 避免运行时类型错误
- 提供最佳性能
### 4. **上下文日志记录**
- 自动从上下文提取 `request_id`, `user_id`, `trace_id`
- 支持 `WithContext()` 方法
- 便于分布式系统追踪
## 🏗️ 架构设计
### 核心接口
```go
type Logger interface {
// 基础日志方法
Debug(msg string, fields ...zapcore.Field)
Info(msg string, fields ...zapcore.Field)
Warn(msg string, fields ...zapcore.Field)
Error(msg string, fields ...zapcore.Field)
Fatal(msg string, fields ...zapcore.Field)
Panic(msg string, fields ...zapcore.Field)
// 结构化日志方法
With(fields ...zapcore.Field) Logger
WithContext(ctx context.Context) Logger
Named(name string) Logger
// 同步和清理
Sync() error
Core() zapcore.Core
// 获取原生 Zap Logger
GetZapLogger() *zap.Logger
}
```
### 实现类型
1. **ZapLogger**: 标准日志器,基于 Zap 官方预设
2. **LevelLogger**: 级别分文件日志器,支持按级别分离
3. **全局日志器**: 通过 `zap.ReplaceGlobals()` 提供全局访问
## 🚀 使用方法
### 1. 基础使用
```go
package main
import (
"go.uber.org/zap"
"tyapi-server/internal/shared/logger"
)
func main() {
// 初始化全局日志器
config := logger.Config{
Development: true,
Output: "stdout",
Format: "console",
}
if err := logger.InitGlobalLogger(config); err != nil {
panic(err)
}
// 使用全局日志器
logger.L().Info("应用启动成功")
// 或者获取全局日志器
globalLogger := logger.GetGlobalLogger()
globalLogger.Info("使用全局日志器")
}
```
### 2. 依赖注入使用
```go
type ProductService struct {
logger logger.Logger
}
func NewProductService(logger logger.Logger) *ProductService {
return &ProductService{logger: logger}
}
func (s *ProductService) CreateProduct(ctx context.Context, product *Product) error {
// 记录操作日志
s.logger.Info("创建产品",
zap.String("product_id", product.ID),
zap.String("product_name", product.Name),
zap.String("user_id", product.CreatedBy),
)
// 业务逻辑...
return nil
}
```
### 3. 上下文日志记录
```go
func (s *ProductService) GetProduct(ctx context.Context, id string) (*Product, error) {
// 自动从上下文提取字段
logger := s.logger.WithContext(ctx)
logger.Info("获取产品信息",
zap.String("product_id", id),
zap.String("operation", "get_product"),
)
// 业务逻辑...
return product, nil
}
```
### 4. 结构化字段
```go
// 使用强类型字段
s.logger.Info("用户登录",
zap.String("username", "john_doe"),
zap.Int("user_id", 12345),
zap.String("ip_address", "192.168.1.100"),
zap.String("user_agent", r.UserAgent()),
zap.Time("login_time", time.Now()),
)
// 记录错误
if err != nil {
s.logger.Error("数据库操作失败",
zap.Error(err),
zap.String("operation", "create_user"),
zap.String("table", "users"),
)
}
```
### 5. 级别分文件日志
```go
// 配置启用级别分文件
config := logger.Config{
EnableLevelSeparation: true,
Output: "file",
LogDir: "logs",
UseDaily: true,
LevelConfigs: map[string]interface{}{
"debug": map[string]interface{}{
"max_size": 50,
"max_backups": 3,
"max_age": 7,
},
"error": map[string]interface{}{
"max_size": 200,
"max_backups": 10,
"max_age": 90,
},
},
}
// 创建级别分文件日志器
levelLogger, err := logger.NewLevelLogger(logger.LevelLoggerConfig{
BaseConfig: config,
EnableLevelSeparation: true,
LevelConfigs: convertLevelConfigs(config.LevelConfigs),
})
```
## 📁 日志文件结构
### 按级别分文件
```
logs/
├── 2024-01-01/
│ ├── debug.log # 调试日志
│ ├── info.log # 信息日志
│ ├── warn.log # 警告日志
│ ├── error.log # 错误日志
│ ├── fatal.log # 致命错误日志
│ └── panic.log # 恐慌错误日志
└── app.log # 主日志文件
```
### 按日期分包
```
logs/
├── 2024-01-01/
│ ├── app.log
│ └── error.log
├── 2024-01-02/
│ ├── app.log
│ └── error.log
└── app.log # 当前日期
```
## ⚙️ 配置选项
### 基础配置
```yaml
logger:
# 环境配置
development: true # 是否为开发环境
# 输出配置
output: "file" # 输出方式: stdout, stderr, file
format: "json" # 输出格式: json, console
log_dir: "logs" # 日志目录
# 文件配置
max_size: 100 # 单个文件最大大小(MB)
max_backups: 5 # 最大备份文件数
max_age: 30 # 最大保留天数
compress: true # 是否压缩
# 高级功能
use_daily: true # 是否按日分包
enable_level_separation: true # 是否启用按级别分文件
use_color: false # 是否使用彩色输出
```
### 级别配置
```yaml
logger:
level_configs:
debug:
max_size: 50 # 50MB
max_backups: 3 # 3个备份
max_age: 7 # 7天
compress: true
info:
max_size: 100 # 100MB
max_backups: 5 # 5个备份
max_age: 30 # 30天
compress: true
error:
max_size: 200 # 200MB
max_backups: 10 # 10个备份
max_age: 90 # 90天
compress: true
```
## 🔧 最佳实践
### 1. **使用强类型字段**
```go
// ✅ 推荐:使用强类型字段
logger.Info("用户操作",
zap.String("user_id", userID),
zap.String("action", "login"),
zap.Time("timestamp", time.Now()),
)
// ❌ 避免:使用 Any 字段
logger.Info("用户操作",
zap.Any("user_id", userID),
zap.Any("action", "login"),
zap.Any("timestamp", time.Now()),
)
```
### 2. **合理使用日志级别**
```go
// Debug: 详细的调试信息
logger.Debug("SQL查询", zap.String("query", sql))
// Info: 重要的业务事件
logger.Info("用户注册成功", zap.String("user_id", userID))
// Warn: 警告信息,不影响功能
logger.Warn("数据库连接池使用率过高", zap.Int("usage", 85))
// Error: 错误信息,功能受影响
logger.Error("数据库连接失败", zap.Error(err))
// Fatal: 致命错误,应用无法继续
logger.Fatal("配置文件加载失败", zap.Error(err))
```
### 3. **上下文信息提取**
```go
// 在中间件中设置上下文
func LoggingMiddleware(logger logger.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
// 生成请求ID
requestID := uuid.New().String()
// 设置上下文
ctx := context.WithValue(c.Request.Context(), "request_id", requestID)
ctx = context.WithValue(ctx, "user_id", getUserID(c))
ctx = context.WithValue(ctx, "trace_id", getTraceID(c))
c.Request = c.Request.WithContext(ctx)
// 记录请求日志
logger.WithContext(ctx).Info("收到请求",
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.String("client_ip", c.ClientIP()),
)
c.Next()
}
}
```
### 4. **性能优化**
```go
// ✅ 推荐:延迟计算
if logger.Core().Enabled(zapcore.DebugLevel) {
logger.Debug("调试信息", zap.String("data", expensiveOperation()))
}
// ❌ 避免:总是计算
logger.Debug("调试信息", zap.String("data", expensiveOperation()))
```
## 🚨 错误处理
### 1. **Panic 恢复**
```go
// 使用 panic 恢复中间件
func PanicRecoveryMiddleware(logger *zap.Logger) gin.HandlerFunc {
return gin.RecoveryWithWriter(&panicLogger{logger: logger})
}
type panicLogger struct {
logger *zap.Logger
}
func (pl *panicLogger) Write(p []byte) (n int, err error) {
pl.logger.Error("系统发生严重错误",
zap.String("error_type", "panic"),
zap.String("stack_trace", string(p)),
zap.String("timestamp", time.Now().Format("2006-01-02 15:04:05")),
)
return len(p), nil
}
```
### 2. **错误日志记录**
```go
// 记录错误详情
if err != nil {
logger.Error("操作失败",
zap.Error(err),
zap.String("operation", "create_user"),
zap.String("user_id", userID),
zap.String("stack_trace", string(debug.Stack())),
)
return err
}
```
## 📊 性能基准
基于 [Zap 官方基准测试](https://betterstack.com/community/guides/logging/go/zap/)
| 包 | 时间 | 相对于 Zap | 内存分配 |
|----|------|------------|----------|
| zap | 193 ns/op | +0% | 0 allocs/op |
| zap (sugared) | 227 ns/op | +18% | 1 allocs/op |
| zerolog | 81 ns/op | -58% | 0 allocs/op |
| slog | 322 ns/op | +67% | 0 allocs/op |
## 🔍 调试和故障排除
### 1. **检查日志级别**
```go
// 检查日志级别是否启用
if logger.Core().Enabled(zapcore.DebugLevel) {
logger.Debug("调试信息")
}
```
### 2. **同步日志**
```go
// 确保日志写入完成
defer logger.Sync()
// 或者在应用关闭时
func cleanup() {
logger.Sync()
}
```
### 3. **验证配置**
```go
// 验证日志器配置
config := logger.Config{
Development: true,
Output: "stdout",
Format: "console",
}
logger, err := logger.NewLogger(config)
if err != nil {
log.Fatalf("创建日志器失败: %v", err)
}
```
## 🎯 总结
本日志系统完全基于 Zap 官方最佳实践设计,具有以下优势:
1. **高性能**: 基于 Zap 的高性能实现
2. **官方推荐**: 使用 `zap.NewProduction()``zap.NewDevelopment()` 预设
3. **强类型**: 支持强类型字段,避免运行时错误
4. **结构化**: 支持结构化日志记录
5. **上下文**: 自动提取上下文信息
6. **灵活配置**: 支持文件输出、级别分离、按日分包等
7. **全局访问**: 支持全局日志器访问
通过合理使用,您将获得高性能、结构化的日志系统,满足生产环境的各种需求!
## 📚 参考资源
- [Zap 官方文档](https://pkg.go.dev/go.uber.org/zap)
- [Zap 最佳实践指南](https://betterstack.com/community/guides/logging/go/zap/)
- [Zap GitHub 仓库](https://github.com/uber-go/zap)