80 lines
2.0 KiB
Go
80 lines
2.0 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
)
|
|
|
|
// LoadFile loads and validates framework configuration using explicit file references.
|
|
func LoadFile(path string) (Config, error) { return LoadProject(path, Default()) }
|
|
|
|
// Reloader only accepts log.level changes. Static changes and invalid files
|
|
// leave the current snapshot and logger untouched. Watch does not own a goroutine.
|
|
type Reloader struct {
|
|
mu sync.Mutex
|
|
path string
|
|
current Config
|
|
logger *logx.Logger
|
|
}
|
|
|
|
func NewReloader(path string, logger *logx.Logger) (*Reloader, error) {
|
|
cfg, err := LoadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = logger.SetLevel(cfg.Log.Level); err != nil {
|
|
return nil, err
|
|
}
|
|
return &Reloader{path: path, current: cfg, logger: logger}, nil
|
|
}
|
|
func (r *Reloader) Reload() error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
next, err := LoadFile(r.path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
comparable := next
|
|
comparable.Log.Level = r.current.Log.Level
|
|
if !reflect.DeepEqual(comparable, r.current) {
|
|
return errors.New("config: static fields changed; restart required")
|
|
}
|
|
if err = r.logger.SetLevel(next.Log.Level); err != nil {
|
|
return err
|
|
}
|
|
r.current = next
|
|
return nil
|
|
}
|
|
func (r *Reloader) Snapshot() Config {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
cfg := r.current
|
|
cfg.Response = r.current.Response.Clone()
|
|
cfg.Errors = r.current.Errors.Clone()
|
|
cfg.TrustedProxies = append([]string(nil), cfg.TrustedProxies...)
|
|
cfg.Log.RedactKeys = append([]string(nil), cfg.Log.RedactKeys...)
|
|
return cfg
|
|
}
|
|
func (r *Reloader) Watch(ctx context.Context, interval time.Duration, onError func(error)) error {
|
|
if ctx == nil || interval < 100*time.Millisecond {
|
|
return errors.New("config: context and interval >=100ms required")
|
|
}
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-ticker.C:
|
|
if err := r.Reload(); err != nil && onError != nil {
|
|
onError(err)
|
|
}
|
|
}
|
|
}
|
|
}
|