github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/config/logging.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package config 5 6 import ( 7 "encoding/json" 8 "errors" 9 "os" 10 "path/filepath" 11 "strings" 12 "sync" 13 14 "github.com/masterhung0112/hk_server/v5/shared/mlog" 15 ) 16 17 type LogSrcListener func(old, new mlog.LogTargetCfg) 18 19 // LogConfigSrc abstracts the Advanced Logging configuration so that implementations can 20 // fetch from file, database, etc. 21 type LogConfigSrc interface { 22 // Get fetches the current, cached configuration. 23 Get() mlog.LogTargetCfg 24 25 // Set updates the dsn specifying the source and reloads 26 Set(dsn string, configStore *Store) (err error) 27 28 // AddListener adds a callback function to invoke when the configuration is modified. 29 AddListener(listener LogSrcListener) string 30 31 // RemoveListener removes a callback function using an id returned from AddListener. 32 RemoveListener(id string) 33 34 // Close cleans up resources. 35 Close() error 36 } 37 38 // NewLogConfigSrc creates an advanced logging configuration source, backed by a 39 // file, JSON string, or database. 40 func NewLogConfigSrc(dsn string, configStore *Store) (LogConfigSrc, error) { 41 if configStore == nil { 42 return nil, errors.New("configStore should not be nil") 43 } 44 45 dsn = strings.TrimSpace(dsn) 46 47 if isJSONMap(dsn) { 48 return newJSONSrc(dsn) 49 } 50 51 path := dsn 52 // If this is a file based config we need the full path so it can be watched. 53 if strings.HasPrefix(configStore.String(), "file://") && !filepath.IsAbs(dsn) { 54 configPath := strings.TrimPrefix(configStore.String(), "file://") 55 path = filepath.Join(filepath.Dir(configPath), dsn) 56 } 57 58 return newFileSrc(path, configStore) 59 } 60 61 // jsonSrc 62 63 type jsonSrc struct { 64 logSrcEmitter 65 mutex sync.RWMutex 66 cfg mlog.LogTargetCfg 67 } 68 69 func newJSONSrc(data string) (*jsonSrc, error) { 70 src := &jsonSrc{} 71 return src, src.Set(data, nil) 72 } 73 74 // Get fetches the current, cached configuration 75 func (src *jsonSrc) Get() mlog.LogTargetCfg { 76 src.mutex.RLock() 77 defer src.mutex.RUnlock() 78 return src.cfg 79 } 80 81 // Set updates the JSON specifying the source and reloads 82 func (src *jsonSrc) Set(data string, _ *Store) error { 83 cfg, err := logTargetCfgFromJSON([]byte(data)) 84 if err != nil { 85 return err 86 } 87 88 src.set(cfg) 89 return nil 90 } 91 92 func (src *jsonSrc) set(cfg mlog.LogTargetCfg) { 93 src.mutex.Lock() 94 defer src.mutex.Unlock() 95 96 old := src.cfg 97 src.cfg = cfg 98 src.invokeConfigListeners(old, cfg) 99 } 100 101 // Close cleans up resources. 102 func (src *jsonSrc) Close() error { 103 return nil 104 } 105 106 // fileSrc 107 108 type fileSrc struct { 109 logSrcEmitter 110 mutex sync.RWMutex 111 cfg mlog.LogTargetCfg 112 113 path string 114 watcher *watcher 115 } 116 117 func newFileSrc(path string, configStore *Store) (*fileSrc, error) { 118 src := &fileSrc{ 119 path: path, 120 } 121 if err := src.Set(path, configStore); err != nil { 122 return nil, err 123 } 124 return src, nil 125 } 126 127 // Get fetches the current, cached configuration 128 func (src *fileSrc) Get() mlog.LogTargetCfg { 129 src.mutex.RLock() 130 defer src.mutex.RUnlock() 131 return src.cfg 132 } 133 134 // Set updates the dsn specifying the file source and reloads. 135 // The file will be watched for changes and reloaded as needed, 136 // and all listeners notified. 137 func (src *fileSrc) Set(path string, configStore *Store) error { 138 data, err := configStore.GetFile(path) 139 if err != nil { 140 return err 141 } 142 143 cfg, err := logTargetCfgFromJSON(data) 144 if err != nil { 145 return err 146 } 147 148 src.set(cfg) 149 150 // If path is a real file and not just the name of a database resource then watch it for changes. 151 // Absolute paths are explicit and require no resolution. 152 if _, err = os.Stat(path); os.IsNotExist(err) { 153 return nil 154 } 155 156 src.mutex.Lock() 157 defer src.mutex.Unlock() 158 159 if src.watcher != nil { 160 if err = src.watcher.Close(); err != nil { 161 mlog.Error("Failed to close watcher", mlog.Err(err)) 162 } 163 src.watcher = nil 164 } 165 166 watcher, err := newWatcher(path, func() { 167 if serr := src.Set(path, configStore); serr != nil { 168 mlog.Error("Failed to reload file on change", mlog.String("path", path), mlog.Err(serr)) 169 } 170 }) 171 if err != nil { 172 return err 173 } 174 175 src.watcher = watcher 176 177 return nil 178 } 179 180 func (src *fileSrc) set(cfg mlog.LogTargetCfg) { 181 src.mutex.Lock() 182 defer src.mutex.Unlock() 183 184 old := src.cfg 185 src.cfg = cfg 186 src.invokeConfigListeners(old, cfg) 187 } 188 189 // Close cleans up resources. 190 func (src *fileSrc) Close() error { 191 var err error 192 src.mutex.Lock() 193 defer src.mutex.Unlock() 194 if src.watcher != nil { 195 err = src.watcher.Close() 196 src.watcher = nil 197 } 198 return err 199 } 200 201 func logTargetCfgFromJSON(data []byte) (mlog.LogTargetCfg, error) { 202 cfg := make(mlog.LogTargetCfg) 203 err := json.Unmarshal(data, &cfg) 204 if err != nil { 205 return nil, err 206 } 207 return cfg, nil 208 }