Files
tyapi-server/docs/Zap官方最佳实践日志系统指南.md

454 lines
11 KiB
Markdown
Raw Permalink Normal View History

# 🚀 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)