github.com/mattermost/mattermost-server/server/v8@v8.0.0-20230610055354-a6d1d38b273d/config/logconfigsrc.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 "path/filepath" 10 "strconv" 11 "strings" 12 "sync" 13 14 "github.com/mattermost/mattermost-server/server/public/shared/mlog" 15 ) 16 17 const ( 18 LogConfigSrcTypeJSON LogConfigSrcType = "json" 19 LogConfigSrcTypeFile LogConfigSrcType = "file" 20 ) 21 22 type LogSrcListener func(old, new mlog.LoggerConfiguration) 23 type LogConfigSrcType string 24 25 // LogConfigSrc abstracts the Advanced Logging configuration so that implementations can 26 // fetch from file, database, etc. 27 type LogConfigSrc interface { 28 // Get fetches the current, cached configuration. 29 Get() mlog.LoggerConfiguration 30 31 // Set updates the dsn specifying the source and reloads 32 Set(dsn []byte, configStore *Store) (err error) 33 34 // GetType returns the type of config source (JSON, file, ...) 35 GetType() LogConfigSrcType 36 37 // Close cleans up resources. 38 Close() error 39 } 40 41 // NewLogConfigSrc creates an advanced logging configuration source, backed by a 42 // file, JSON string, or database. 43 func NewLogConfigSrc(dsn json.RawMessage, configStore *Store) (LogConfigSrc, error) { 44 if len(dsn) == 0 { 45 return nil, errors.New("dsn should not be empty") 46 } 47 48 if configStore == nil { 49 return nil, errors.New("configStore should not be nil") 50 } 51 52 // check if embedded JSON 53 if isJSONMap(dsn) { 54 return newJSONSrc(dsn) 55 } 56 57 // Now we're treating the DSN as a string which may contain escaped JSON or be a filespec. 58 str := strings.TrimSpace(string(dsn)) 59 if s, err := strconv.Unquote(str); err == nil { 60 str = s 61 } 62 63 // check if escaped JSON 64 strBytes := []byte(str) 65 if isJSONMap(strBytes) { 66 return newJSONSrc(strBytes) 67 } 68 69 // If this is a file based config we need the full path so it can be watched. 70 path := str 71 if strings.HasPrefix(configStore.String(), "file://") && !filepath.IsAbs(path) { 72 configPath := strings.TrimPrefix(configStore.String(), "file://") 73 path = filepath.Join(filepath.Dir(configPath), path) 74 } 75 76 return newFileSrc(path, configStore) 77 } 78 79 // jsonSrc 80 81 type jsonSrc struct { 82 logSrcEmitter 83 mutex sync.RWMutex 84 cfg mlog.LoggerConfiguration 85 } 86 87 func newJSONSrc(data json.RawMessage) (*jsonSrc, error) { 88 src := &jsonSrc{} 89 return src, src.Set(data, nil) 90 } 91 92 // Get fetches the current, cached configuration 93 func (src *jsonSrc) Get() mlog.LoggerConfiguration { 94 src.mutex.RLock() 95 defer src.mutex.RUnlock() 96 return src.cfg 97 } 98 99 // Set updates the JSON specifying the source and reloads 100 func (src *jsonSrc) Set(data []byte, _ *Store) error { 101 cfg, err := logTargetCfgFromJSON(data) 102 if err != nil { 103 return err 104 } 105 106 src.set(cfg) 107 return nil 108 } 109 110 // GetType returns the config source type. 111 func (src *jsonSrc) GetType() LogConfigSrcType { 112 return LogConfigSrcTypeJSON 113 } 114 115 func (src *jsonSrc) set(cfg mlog.LoggerConfiguration) { 116 src.mutex.Lock() 117 defer src.mutex.Unlock() 118 119 old := src.cfg 120 src.cfg = cfg 121 src.invokeConfigListeners(old, cfg) 122 } 123 124 // Close cleans up resources. 125 func (src *jsonSrc) Close() error { 126 return nil 127 } 128 129 // fileSrc 130 131 type fileSrc struct { 132 mutex sync.RWMutex 133 cfg mlog.LoggerConfiguration 134 path string 135 } 136 137 func newFileSrc(path string, configStore *Store) (*fileSrc, error) { 138 src := &fileSrc{ 139 path: path, 140 } 141 if err := src.Set([]byte(path), configStore); err != nil { 142 return nil, err 143 } 144 return src, nil 145 } 146 147 // Get fetches the current, cached configuration 148 func (src *fileSrc) Get() mlog.LoggerConfiguration { 149 src.mutex.RLock() 150 defer src.mutex.RUnlock() 151 return src.cfg 152 } 153 154 // Set updates the dsn specifying the file source and reloads. 155 // The file will be watched for changes and reloaded as needed, 156 // and all listeners notified. 157 func (src *fileSrc) Set(path []byte, configStore *Store) error { 158 data, err := configStore.GetFile(string(path)) 159 if err != nil { 160 return err 161 } 162 163 cfg, err := logTargetCfgFromJSON(data) 164 if err != nil { 165 return err 166 } 167 168 src.set(cfg) 169 return nil 170 } 171 172 // GetType returns the config source type. 173 func (src *fileSrc) GetType() LogConfigSrcType { 174 return LogConfigSrcTypeFile 175 } 176 177 func (src *fileSrc) set(cfg mlog.LoggerConfiguration) { 178 src.mutex.Lock() 179 defer src.mutex.Unlock() 180 181 src.cfg = cfg 182 } 183 184 // Close cleans up resources. 185 func (src *fileSrc) Close() error { 186 return nil 187 } 188 189 func logTargetCfgFromJSON(data []byte) (mlog.LoggerConfiguration, error) { 190 cfg := make(mlog.LoggerConfiguration) 191 err := json.Unmarshal(data, &cfg) 192 if err != nil { 193 return nil, err 194 } 195 return cfg, nil 196 }