DESIGN.md 9.2 KB

符合Go理念的日志系统设计

Go语言核心理念遵循

1. 简单性 (Simplicity)

  • 最小API: 只提供必要的方法,避免过度设计
  • 清晰的职责分离: 每个组件职责单一
  • 直观的接口: 接口设计符合Go习惯

2. 可组合性 (Composition)

  • 接口组合: 使用小接口,支持组合使用
  • 选项模式: 使用函数选项模式进行配置
  • 中间件模式: 支持HTTP中间件组合

3. 并发安全 (Concurrency)

  • goroutine安全: 所有组件都是并发安全的
  • 上下文支持: 集成context.Context
  • 优雅关闭: 支持优雅关闭和资源清理

4. 错误处理 (Error Handling)

  • 显式错误: 遵循Go的显式错误处理
  • 错误包装: 使用fmt.Errorf进行错误包装
  • 错误检查: 提供便捷的错误检查方法

5. 接口导向 (Interface-oriented)

  • 小接口: 设计小而专注的接口
  • 依赖注入: 通过接口进行依赖注入
  • 可测试性: 接口设计便于单元测试

重新设计的架构

核心接口

Logger 接口 - 简单而强大

type Logger interface {
    // 核心日志方法
    Debug(msg string, fields ...zap.Field)
    Info(msg string, fields ...zap.Field)
    Warn(msg string, fields ...zap.Field)
    Error(msg string, fields ...zap.Field)
    Fatal(msg string, fields ...zap.Field)

    // 便捷方法
    With(fields ...zap.Field) Logger
    Named(name string) Logger

    // 同步方法
    Sync() error
}

AuditLogger 接口 - 审计专用

type AuditLogger interface {
    // 记录审计事件
    Record(entry *AuditEntry) error

    // 查询审计日志
    Query(filter *AuditFilter) ([]*AuditEntry, error)

    // 清理过期数据
    Cleanup(retention time.Duration) error

    // 同步方法
    Sync() error
}

选项模式配置

Logger 配置选项

type LoggerOption func(*loggerConfig)

func WithLevel(level zapcore.Level) LoggerOption {
    return func(c *loggerConfig) {
        c.level = level
    }
}

func WithOutputPaths(paths ...string) LoggerOption {
    return func(c *loggerConfig) {
        c.outputPaths = paths
    }
}

func WithDevelopment(dev bool) LoggerOption {
    return func(c *loggerConfig) {
        c.development = dev
    }
}

Audit 配置选项

type AuditOption func(*auditConfig)

func WithDatabasePath(path string) AuditOption {
    return func(c *auditConfig) {
        c.dbPath = path
    }
}

func WithRetention(days int) AuditOption {
    return func(c *auditConfig) {
        c.retentionDays = days
    }
}

构造函数设计

Logger 构造函数

func NewLogger(opts ...LoggerOption) (Logger, error) {
    config := &loggerConfig{
        level:       zapcore.InfoLevel,
        development: false,
        encoding:    "json",
        outputPaths: []string{"stdout"},
    }

    for _, opt := range opts {
        opt(config)
    }

    return newZapLogger(config)
}

AuditLogger 构造函数

func NewAuditLogger(opts ...AuditOption) (AuditLogger, error) {
    config := &auditConfig{
        enabled:       true,
        dbPath:        "audit.db",
        retentionDays: 90,
        bufferSize:    1000,
    }

    for _, opt := range opts {
        opt(config)
    }

    return newSQLiteAuditLogger(config)
}

数据结构优化

AuditEntry - 符合Go习惯

type AuditEntry struct {
    ID        string    `json:"id"`
    Timestamp time.Time `json:"timestamp"`
    UserID    string    `json:"user_id,omitempty"`
    Username  string    `json:"username,omitempty"`
    Action    string    `json:"action"`
    Resource  string    `json:"resource,omitempty"`
    Details   string    `json:"details,omitempty"`
    IP        string    `json:"ip,omitempty"`
    UserAgent string    `json:"user_agent,omitempty"`
    Success   bool      `json:"success"`
    Error     string    `json:"error,omitempty"`
}

AuditFilter - 查询过滤器

type AuditFilter struct {
    UserID    string
    Action    string
    Resource  string
    StartTime *time.Time
    EndTime   *time.Time
    Success   *bool
    Limit     int
    Offset    int
}

符合Go习惯的使用方式

基本使用

// 创建日志器
logger, err := NewLogger(
    WithLevel(zapcore.DebugLevel),
    WithDevelopment(true),
    WithOutputPaths("stdout", "app.log"),
)
if err != nil {
    panic(err)
}
defer logger.Sync()

// 使用日志
logger.Info("用户登录",
    zap.String("user_id", "123"),
    zap.String("ip", "192.168.1.1"))

审计使用

// 创建审计日志器
audit, err := NewAuditLogger(
    WithDatabasePath("audit.db"),
    WithRetention(90),
)
if err != nil {
    panic(err)
}
defer audit.Sync()

// 记录审计事件
err = audit.Record(&AuditEntry{
    UserID:   "123",
    Username: "john_doe",
    Action:   "login",
    IP:       "192.168.1.1",
    Success:  true,
})

上下文使用

// 创建带上下文的日志器
userLogger := logger.Named("user_service").With(
    zap.String("user_id", "123"),
    zap.String("request_id", "req_001"),
)

// 在函数中使用
func processUser(ctx context.Context, userID string) error {
    logger := userLogger.With(zap.String("operation", "process_user"))

    logger.Info("开始处理用户")
    defer logger.Info("处理用户完成")

    // 处理逻辑
    if err := doSomething(); err != nil {
        logger.Error("处理失败", zap.Error(err))
        return err
    }

    return nil
}

HTTP中间件

func AuditMiddleware(audit AuditLogger) gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()

        // 处理请求
        c.Next()

        // 记录审计
        entry := &AuditEntry{
            UserID:    getUserID(c),
            Action:    c.Request.Method + " " + c.Request.URL.Path,
            IP:        c.ClientIP(),
            UserAgent: c.Request.UserAgent(),
            Success:   c.Writer.Status() < 400,
        }

        if err := audit.Record(entry); err != nil {
            // 审计失败不应该影响正常业务
            c.Error(err)
        }

        // 记录访问日志
        duration := time.Since(start)
        logger.Info("HTTP请求",
            zap.String("method", c.Request.Method),
            zap.String("path", c.Request.URL.Path),
            zap.Int("status", c.Writer.Status()),
            zap.Duration("duration", duration),
        )
    }
}

错误处理设计

自定义错误类型

type LoggerError struct {
    Op  string // 操作
    Err error  // 底层错误
}

func (e *LoggerError) Error() string {
    return fmt.Sprintf("logger %s: %v", e.Op, e.Err)
}

func (e *LoggerError) Unwrap() error {
    return e.Err
}

便捷的错误检查

// 检查是否为日志错误
var loggerErr *LoggerError
if errors.As(err, &loggerErr) {
    // 处理日志相关错误
}

// 检查特定操作的错误
if errors.Is(err, ErrAuditDisabled) {
    // 审计功能被禁用
}

测试设计

单元测试

func TestLogger(t *testing.T) {
    logger, err := NewLogger(WithDevelopment(true))
    require.NoError(t, err)
    defer logger.Sync()

    // 测试日志输出
    logger.Info("测试消息", zap.String("key", "value"))

    // 验证日志内容(通过钩子或缓冲区)
}

集成测试

func TestAuditLogger(t *testing.T) {
    audit, err := NewAuditLogger(WithDatabasePath(":memory:"))
    require.NoError(t, err)
    defer audit.Sync()

    // 插入测试数据
    entry := &AuditEntry{
        UserID:  "test_user",
        Action:  "test_action",
        Success: true,
    }

    err = audit.Record(entry)
    require.NoError(t, err)

    // 查询验证
    entries, err := audit.Query(&AuditFilter{
        UserID: "test_user",
        Limit:  10,
    })
    require.NoError(t, err)
    require.Len(t, entries, 1)
}

性能优化

对象池

var auditEntryPool = sync.Pool{
    New: func() interface{} {
        return &AuditEntry{}
    },
}

func getAuditEntry() *AuditEntry {
    return auditEntryPool.Get().(*AuditEntry)
}

func putAuditEntry(entry *AuditEntry) {
    // 重置对象
    *entry = AuditEntry{}
    auditEntryPool.Put(entry)
}

异步写入

type asyncAuditLogger struct {
    AuditLogger
    entries chan *AuditEntry
    done    chan struct{}
}

func (a *asyncAuditLogger) Record(entry *AuditEntry) error {
    select {
    case a.entries <- entry:
        return nil
    case <-a.done:
        return ErrLoggerClosed
    default:
        return ErrBufferFull
    }
}

文档和示例

包文档

// Package logger 提供基于Zap的高性能日志和审计功能
//
// 设计理念:
//   - 简单性: 最小化API,专注于核心功能
//   - 性能: 利用Zap的高性能特性
//   - 可组合性: 支持中间件和选项模式
//   - 并发安全: 所有操作都是goroutine安全的
//
// 基本使用:
//
//	logger, _ := logger.NewLogger(logger.WithDevelopment(true))
//	logger.Info("应用启动")
//	defer logger.Sync()
//
// 审计使用:
//
//	audit, _ := logger.NewAuditLogger(logger.WithRetention(90))
//	audit.Record(&logger.AuditEntry{Action: "login", Success: true})
package logger

这个重新设计的日志系统完全遵循Go语言的核心理念,提供了简单、高效、可组合的日志和审计解决方案。