reload

package
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2025 License: Apache-2.0 Imports: 15 Imported by: 0

README

Reload Package

动态配置重载包,提供运行时配置变更功能,支持文件监控、信号处理和 API 触发等多种重载方式。

概述

reload 包实现了完整的动态配置管理功能:

  • 多触发方式:文件监控、信号处理、API 调用
  • 配置验证:重载前的配置验证和回滚机制
  • 备份管理:自动备份历史配置,支持回滚
  • 优雅处理:异步处理,不阻塞主进程
  • 错误恢复:配置失败时的自动恢复机制
  • 生产就绪:完整的错误处理和日志记录

核心组件

ConfigReloader
type ConfigReloader struct {
    // 内部状态管理
    config        *ReloadConfig
    currentConfig *config.Config
    factory       *factory.LoggerFactory
    
    // 监控组件
    watcher    *fsnotify.Watcher
    signalChan chan os.Signal
    reloadChan chan *config.Config
    
    // 备份和错误处理
    backupConfigs []*config.Config
    errorHandler  *errors.ErrorHandler
}
ReloadConfig
type ReloadConfig struct {
    ConfigFile           string          // 配置文件路径
    Triggers            ReloadTrigger   // 触发方式
    Signals             []os.Signal     // 监听的信号
    ValidateBeforeReload bool           // 重载前验证
    ValidationFunc      ValidationFunc  // 自定义验证函数
    ReloadTimeout       time.Duration   // 重载超时时间
    BackupOnReload      bool           // 是否备份旧配置
    BackupRetention     int            // 备份保留数量
    Callback            ReloadCallback  // 重载完成回调
    Logger              core.Logger     // 内部日志记录器
}

触发方式

ReloadTrigger 类型
const (
    TriggerNone      ReloadTrigger = 0
    TriggerSignal    ReloadTrigger = 1 << iota  // 信号触发
    TriggerFileWatch                           // 文件监控触发  
    TriggerAPI                                 // API 调用触发
    TriggerAll = TriggerSignal | TriggerFileWatch | TriggerAPI
)

使用方式

1. 基本文件监控重载
package main

import (
    "github.com/kart-io/logger/reload"
    "github.com/kart-io/logger/config"
    "github.com/kart-io/logger/factory"
    "github.com/kart-io/logger/option"
)

func main() {
    // 初始配置
    initialConfig := config.DefaultConfig()
    
    // 创建工厂
    opt := option.DefaultLogOption()
    factory := factory.NewLoggerFactory(opt)
    
    // 配置重载器
    reloadConfig := &reload.ReloadConfig{
        ConfigFile:           "logger.yaml",
        Triggers:            reload.TriggerFileWatch,
        ValidateBeforeReload: true,
        BackupOnReload:      true,
        BackupRetention:     5,
    }
    
    // 创建重载器
    reloader, err := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    if err != nil {
        panic(err)
    }
    
    // 启动重载器
    if err := reloader.Start(); err != nil {
        panic(err)
    }
    defer reloader.Stop()
    
    // 应用继续运行,配置文件变更时会自动重载
    select {}
}
2. 信号处理重载
func signalReloadExample() {
    reloadConfig := &reload.ReloadConfig{
        ConfigFile: "logger.yaml",
        Triggers:   reload.TriggerSignal,
        Signals:    []os.Signal{syscall.SIGUSR1, syscall.SIGHUP},
    }
    
    reloader, _ := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    reloader.Start()
    
    // 现在可以通过信号触发重载:
    // kill -USR1 <pid>  # 触发配置重载
    // kill -HUP <pid>   # 触发配置重载
}
3. API 触发重载
func apiReloadExample() {
    reloadConfig := &reload.ReloadConfig{
        Triggers: reload.TriggerAPI,
    }
    
    reloader, _ := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    reloader.Start()
    
    // HTTP API 端点
    http.HandleFunc("/reload", func(w http.ResponseWriter, r *http.Request) {
        // 解析新配置
        var newConfig config.Config
        if err := json.NewDecoder(r.Body).Decode(&newConfig); err != nil {
            http.Error(w, "Invalid config", http.StatusBadRequest)
            return
        }
        
        // 触发重载
        if err := reloader.TriggerReload(&newConfig); err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(map[string]string{"status": "reloaded"})
    })
}
4. 综合使用(多触发方式)
func comprehensiveReloadExample() {
    // 创建日志器用于重载器内部日志
    logger, _ := logger.NewWithDefaults()
    
    reloadConfig := &reload.ReloadConfig{
        ConfigFile:           "logger.yaml",
        Triggers:            reload.TriggerAll, // 启用所有触发方式
        Signals:             []os.Signal{syscall.SIGUSR1, syscall.SIGHUP},
        ValidateBeforeReload: true,
        ValidationFunc:      customValidationFunc,
        ReloadTimeout:       30 * time.Second,
        BackupOnReload:      true,
        BackupRetention:     10,
        Callback:            reloadCallback,
        Logger:              logger,
    }
    
    reloader, err := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    if err != nil {
        panic(err)
    }
    
    if err := reloader.Start(); err != nil {
        panic(err)
    }
    
    // 现在支持:
    // 1. 文件变更自动重载
    // 2. 信号触发重载 (kill -USR1 <pid>)
    // 3. API 调用重载
    
    defer reloader.Stop()
}

// 自定义验证函数
func customValidationFunc(cfg *config.Config) error {
    if cfg.Engine != "zap" && cfg.Engine != "slog" {
        return fmt.Errorf("unsupported engine: %s", cfg.Engine)
    }
    
    if cfg.Level == "" {
        return fmt.Errorf("log level cannot be empty")
    }
    
    return nil
}

// 重载完成回调
func reloadCallback(oldConfig, newConfig *config.Config) error {
    fmt.Printf("配置重载完成: %s -> %s\n", oldConfig.Engine, newConfig.Engine)
    
    // 可以执行额外的重载逻辑
    // 比如通知其他组件、更新监控指标等
    
    return nil
}

配置文件格式

YAML 配置示例
# logger.yaml
engine: "zap"
level: "info"
format: "json"
output-paths: 
  - "stdout"
  - "/var/log/app.log"

# OTLP 配置
otlp-endpoint: "http://localhost:4317"
otlp:
  protocol: "grpc"
  timeout: "10s"
  headers:
    Authorization: "Bearer token123"

# 功能开关
development: false
disable-caller: false
disable-stacktrace: false
JSON 配置示例
{
  "engine": "slog",
  "level": "debug",
  "format": "console",
  "output_paths": ["stdout"],
  "otlp_endpoint": "http://localhost:4317",
  "development": true
}

备份和回滚

查看备份配置
func backupManagementExample(reloader *reload.ConfigReloader) {
    // 获取当前配置
    currentConfig := reloader.GetCurrentConfig()
    fmt.Printf("当前引擎: %s, 级别: %s\n", currentConfig.Engine, currentConfig.Level)
    
    // 获取所有备份配置
    backups := reloader.GetBackupConfigs()
    fmt.Printf("备份配置数量: %d\n", len(backups))
    
    for i, backup := range backups {
        fmt.Printf("备份 %d: 引擎=%s, 级别=%s\n", i, backup.Engine, backup.Level)
    }
}
回滚到上一个配置
func rollbackExample(reloader *reload.ConfigReloader) {
    // 回滚到上一个配置
    if err := reloader.RollbackToPrevious(); err != nil {
        fmt.Printf("回滚失败: %v\n", err)
    } else {
        fmt.Println("成功回滚到上一个配置")
    }
    
    // 查看回滚后的配置
    currentConfig := reloader.GetCurrentConfig()
    fmt.Printf("回滚后引擎: %s\n", currentConfig.Engine)
}

错误处理和监控

错误处理策略
func errorHandlingExample() {
    reloadConfig := &reload.ReloadConfig{
        ConfigFile:           "logger.yaml",
        Triggers:            reload.TriggerFileWatch,
        ValidateBeforeReload: true,
        ValidationFunc: func(cfg *config.Config) error {
            // 严格验证
            if cfg.Engine == "" {
                return fmt.Errorf("engine cannot be empty")
            }
            
            // 检查 OTLP 配置
            if cfg.IsOTLPEnabled() && cfg.OTLP.Endpoint == "" {
                return fmt.Errorf("OTLP enabled but endpoint is empty")
            }
            
            return nil
        },
        ReloadTimeout:   15 * time.Second,
        BackupOnReload:  true,
        BackupRetention: 3,
        
        // 重载完成回调,用于错误通知
        Callback: func(oldConfig, newConfig *config.Config) error {
            // 发送重载成功通知
            notifyConfigReload(oldConfig, newConfig)
            return nil
        },
    }
    
    reloader, err := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    if err != nil {
        handleReloaderInitError(err)
        return
    }
    
    if err := reloader.Start(); err != nil {
        handleReloaderStartError(err)
        return
    }
}

func handleReloaderInitError(err error) {
    // 记录初始化错误
    log.Errorf("重载器初始化失败: %v", err)
    
    // 可能需要降级处理,比如禁用动态配置功能
    runWithStaticConfig()
}

func handleReloaderStartError(err error) {
    // 记录启动错误
    log.Errorf("重载器启动失败: %v", err)
    
    // 尝试重新启动或降级
    retryReloaderStart()
}
监控集成
func monitoringIntegrationExample(reloader *reload.ConfigReloader) {
    // 设置监控指标
    reloadConfig := &reload.ReloadConfig{
        Callback: func(oldConfig, newConfig *config.Config) error {
            // 更新监控指标
            metrics.IncrementCounter("config_reloads_total", 1)
            metrics.RecordHistogram("config_reload_duration", time.Since(start))
            
            // 记录配置变更
            if oldConfig.Engine != newConfig.Engine {
                metrics.IncrementCounter("config_engine_changes", 1, 
                    map[string]string{
                        "from": oldConfig.Engine,
                        "to":   newConfig.Engine,
                    })
            }
            
            if oldConfig.Level != newConfig.Level {
                metrics.IncrementCounter("config_level_changes", 1,
                    map[string]string{
                        "from": oldConfig.Level,
                        "to":   newConfig.Level,
                    })
            }
            
            return nil
        },
        
        ValidationFunc: func(cfg *config.Config) error {
            start := time.Now()
            defer func() {
                metrics.RecordHistogram("config_validation_duration", time.Since(start))
            }()
            
            if err := cfg.Validate(); err != nil {
                metrics.IncrementCounter("config_validation_errors", 1)
                return err
            }
            
            metrics.IncrementCounter("config_validations_success", 1)
            return nil
        },
    }
}

HTTP API 集成

完整的 Web 管理接口
func setupReloadAPI(reloader *reload.ConfigReloader) {
    // 获取当前配置
    http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "GET":
            config := reloader.GetCurrentConfig()
            w.Header().Set("Content-Type", "application/json")
            json.NewEncoder(w).Encode(config)
            
        case "PUT":
            var newConfig config.Config
            if err := json.NewDecoder(r.Body).Decode(&newConfig); err != nil {
                http.Error(w, "Invalid JSON", http.StatusBadRequest)
                return
            }
            
            if err := reloader.TriggerReload(&newConfig); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            
            w.WriteHeader(http.StatusOK)
            json.NewEncoder(w).Encode(map[string]string{"status": "reloaded"})
        }
    })
    
    // 获取备份配置
    http.HandleFunc("/config/backups", func(w http.ResponseWriter, r *http.Request) {
        backups := reloader.GetBackupConfigs()
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]interface{}{
            "count":   len(backups),
            "backups": backups,
        })
    })
    
    // 回滚配置
    http.HandleFunc("/config/rollback", func(w http.ResponseWriter, r *http.Request) {
        if r.Method != "POST" {
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
            return
        }
        
        if err := reloader.RollbackToPrevious(); err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        currentConfig := reloader.GetCurrentConfig()
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]interface{}{
            "status": "rolled_back",
            "config": currentConfig,
        })
    })
    
    // 健康检查
    http.HandleFunc("/config/health", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]interface{}{
            "status":        "healthy",
            "backup_count":  len(reloader.GetBackupConfigs()),
            "current_engine": reloader.GetCurrentConfig().Engine,
        })
    })
}

最佳实践

1. 生产环境配置
func productionReloadSetup() *reload.ConfigReloader {
    // 生产环境推荐配置
    reloadConfig := &reload.ReloadConfig{
        ConfigFile:           "/etc/myapp/logger.yaml",
        Triggers:            reload.TriggerSignal | reload.TriggerAPI, // 不用文件监控避免意外重载
        Signals:             []os.Signal{syscall.SIGUSR1},            // 只监听 USR1
        ValidateBeforeReload: true,                                   // 必须验证
        ValidationFunc:      strictValidationFunc,                   // 严格验证
        ReloadTimeout:       10 * time.Second,                       // 较短超时
        BackupOnReload:      true,                                   // 必须备份
        BackupRetention:     3,                                      // 保留最近3个
        Callback:           productionCallback,                     // 生产回调
        Logger:             productionLogger,                       // 生产日志器
    }
    
    reloader, err := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    if err != nil {
        log.Fatalf("Failed to create config reloader: %v", err)
    }
    
    return reloader
}

func strictValidationFunc(cfg *config.Config) error {
    // 生产环境严格验证
    if cfg.Development {
        return fmt.Errorf("development mode not allowed in production")
    }
    
    if cfg.Level == "DEBUG" {
        return fmt.Errorf("debug level not allowed in production")
    }
    
    // 确保有适当的输出路径
    hasFile := false
    for _, path := range cfg.OutputPaths {
        if path != "stdout" && path != "stderr" {
            hasFile = true
            break
        }
    }
    if !hasFile {
        return fmt.Errorf("production requires file output")
    }
    
    return nil
}
2. 开发环境配置
func developmentReloadSetup() *reload.ConfigReloader {
    // 开发环境配置
    reloadConfig := &reload.ReloadConfig{
        ConfigFile:           "logger-dev.yaml",
        Triggers:            reload.TriggerAll,           // 启用所有触发方式
        ValidateBeforeReload: false,                     // 宽松验证
        ReloadTimeout:       30 * time.Second,           // 长超时
        BackupOnReload:      true,
        BackupRetention:     10,                         // 更多备份
        Logger:              developmentLogger,
    }
    
    reloader, _ := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    return reloader
}
3. 容器化部署
func containerReloadSetup() *reload.ConfigReloader {
    reloadConfig := &reload.ReloadConfig{
        Triggers: reload.TriggerSignal | reload.TriggerAPI,
        Signals:  []os.Signal{syscall.SIGUSR1},
        // 容器中通常不用文件监控,而是通过信号或API
        ValidateBeforeReload: true,
        BackupOnReload:      true,
        BackupRetention:     5,
    }
    
    reloader, _ := reload.NewConfigReloader(reloadConfig, initialConfig, factory)
    return reloader
}

故障处理

常见问题及解决
  1. 配置文件不存在
// 检查文件是否存在
if _, err := os.Stat(configFile); os.IsNotExist(err) {
    log.Warnf("Config file %s does not exist, using defaults", configFile)
    useDefaultConfig()
}
  1. 配置验证失败
ValidationFunc: func(cfg *config.Config) error {
    if err := cfg.Validate(); err != nil {
        // 记录验证错误但不阻止启动
        log.Errorf("Config validation failed: %v, using previous config", err)
        return err
    }
    return nil
}
  1. 重载超时处理
reloadConfig.ReloadTimeout = 30 * time.Second
reloadConfig.Callback = func(oldConfig, newConfig *config.Config) error {
    // 设置超时上下文
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    
    return applyConfigWithTimeout(ctx, newConfig)
}

API 参考

主要方法
方法 描述
NewConfigReloader(config, initial, factory) 创建配置重载器
Start() 启动重载器
Stop() 停止重载器
TriggerReload(config) 手动触发重载
GetCurrentConfig() 获取当前配置
GetBackupConfigs() 获取备份配置
RollbackToPrevious() 回滚到上一个配置
配置选项
选项 类型 描述
ConfigFile string 配置文件路径
Triggers ReloadTrigger 触发方式位掩码
Signals []os.Signal 监听的信号列表
ValidateBeforeReload bool 重载前是否验证
ValidationFunc ValidationFunc 自定义验证函数
ReloadTimeout time.Duration 重载超时时间
BackupOnReload bool 是否备份旧配置
BackupRetention int 备份保留数量
Callback ReloadCallback 重载完成回调
Logger core.Logger 内部日志记录器

注意事项

  1. 线程安全:所有公共方法都是线程安全的
  2. 资源清理:确保调用 Stop() 清理资源
  3. 文件权限:确保有读取配置文件的权限
  4. 信号处理:避免信号冲突,选择合适的信号
  5. 备份管理:合理设置备份保留数量,避免内存泄漏
  6. 错误处理:实现适当的错误处理和降级策略

相关包

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ConfigReloader

type ConfigReloader struct {
	// contains filtered or unexported fields
}

ConfigReloader manages dynamic configuration reloading

func NewConfigReloader

func NewConfigReloader(reloadConfig *ReloadConfig, initialConfig *option.LogOption, factory *factory.LoggerFactory) (*ConfigReloader, error)

NewConfigReloader creates a new configuration reloader

func (*ConfigReloader) GetBackupConfigs

func (r *ConfigReloader) GetBackupConfigs() []*option.LogOption

GetBackupConfigs returns the backup configurations

func (*ConfigReloader) GetCurrentConfig

func (r *ConfigReloader) GetCurrentConfig() *option.LogOption

GetCurrentConfig returns the current configuration

func (*ConfigReloader) RollbackToPrevious

func (r *ConfigReloader) RollbackToPrevious() error

RollbackToPrevious rolls back to the previous configuration

func (*ConfigReloader) Start

func (r *ConfigReloader) Start() error

Start begins the configuration reloader

func (*ConfigReloader) Stop

func (r *ConfigReloader) Stop() error

Stop stops the configuration reloader

func (*ConfigReloader) TriggerReload

func (r *ConfigReloader) TriggerReload(newConfig *option.LogOption) error

TriggerReload manually triggers a configuration reload

type ReloadCallback

type ReloadCallback func(oldConfig, newConfig *option.LogOption) error

ReloadCallback is called when configuration is successfully reloaded

type ReloadConfig

type ReloadConfig struct {
	// ConfigFile path to watch for changes
	ConfigFile string

	// Triggers specifies which reload triggers to enable
	Triggers ReloadTrigger

	// Signals to listen for (default: SIGUSR1, SIGHUP)
	Signals []os.Signal

	// ValidateBeforeReload validates config before applying
	ValidateBeforeReload bool

	// ValidationFunc custom validation function
	ValidationFunc ValidationFunc

	// ReloadTimeout maximum time to wait for reload to complete
	ReloadTimeout time.Duration

	// BackupOnReload whether to backup the current config before reload
	BackupOnReload bool

	// BackupRetention number of backup configurations to keep
	BackupRetention int

	// Callback function called after successful reload
	Callback ReloadCallback

	// Logger for internal logging
	Logger core.Logger
}

ReloadConfig holds configuration for the reloader

func DefaultReloadConfig

func DefaultReloadConfig() *ReloadConfig

DefaultReloadConfig returns default reload configuration

type ReloadTrigger

type ReloadTrigger int

ReloadTrigger represents different ways to trigger configuration reload

const (
	TriggerNone   ReloadTrigger = 0
	TriggerSignal ReloadTrigger = 1 << iota
	TriggerFileWatch
	TriggerAPI
	TriggerAll = TriggerSignal | TriggerFileWatch | TriggerAPI
)

func (ReloadTrigger) String

func (t ReloadTrigger) String() string

type ValidationFunc

type ValidationFunc func(*option.LogOption) error

ValidationFunc validates a configuration before applying it

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL