github.com/alibaba/sentinel-golang@v1.0.4/logging/logging.go (about) 1 // Copyright 1999-2020 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package logging 16 17 import ( 18 "encoding/json" 19 "errors" 20 "fmt" 21 "log" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "sync" 27 "time" 28 ) 29 30 // Level represents the level of logging. 31 type Level uint8 32 33 const ( 34 DebugLevel Level = iota 35 InfoLevel 36 WarnLevel 37 ErrorLevel 38 ) 39 40 const ( 41 // RecordLogFileName represents the default file name of the record log. 42 RecordLogFileName = "sentinel-record.log" 43 GlobalCallerDepth = 4 44 45 defaultLogMsgBufferSize = 256 46 ) 47 48 var ( 49 DefaultDirName = filepath.Join("logs", "csp") 50 ) 51 52 var ( 53 globalLogLevel = InfoLevel 54 globalLogger = NewConsoleLogger() 55 56 FrequentErrorOnce = &sync.Once{} 57 ) 58 59 // GetGlobalLoggerLevel gets the Sentinel log level 60 func GetGlobalLoggerLevel() Level { 61 return globalLogLevel 62 } 63 64 // ResetGlobalLoggerLevel sets the Sentinel log level 65 // Note: this function is not thread-safe. 66 func ResetGlobalLoggerLevel(l Level) { 67 globalLogLevel = l 68 } 69 70 // GetGlobalLogger gets the Sentinel global logger 71 func GetGlobalLogger() Logger { 72 return globalLogger 73 } 74 75 // ResetGlobalLogger sets the Sentinel global logger 76 // Note: this function is not thread-safe. 77 func ResetGlobalLogger(log Logger) error { 78 if log == nil { 79 return errors.New("nil logger") 80 } 81 globalLogger = log 82 return nil 83 } 84 85 func NewConsoleLogger() Logger { 86 return &DefaultLogger{ 87 log: log.New(os.Stdout, "", 0), 88 } 89 } 90 91 // outputFile is the full path(absolute path) 92 func NewSimpleFileLogger(filepath string) (Logger, error) { 93 logFile, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777) 94 if err != nil { 95 return nil, err 96 } 97 return &DefaultLogger{ 98 log: log.New(logFile, "", 0), 99 }, nil 100 } 101 102 type Logger interface { 103 Debug(msg string, keysAndValues ...interface{}) 104 DebugEnabled() bool 105 106 // Info logs a non-error message with the given key/value pairs as context. 107 // 108 // The msg argument should be used to add some constant description to 109 // the log line. The key/value pairs can then be used to add additional 110 // variable information. The key/value pairs should alternate string 111 // keys and arbitrary values. 112 Info(msg string, keysAndValues ...interface{}) 113 InfoEnabled() bool 114 115 Warn(msg string, keysAndValues ...interface{}) 116 WarnEnabled() bool 117 118 Error(err error, msg string, keysAndValues ...interface{}) 119 ErrorEnabled() bool 120 } 121 122 // sentinel general logger 123 type DefaultLogger struct { 124 log *log.Logger 125 } 126 127 func caller(depth int) (file string, line int) { 128 _, file, line, ok := runtime.Caller(depth) 129 if !ok { 130 file = "???" 131 line = 0 132 } 133 134 // extract 135 if osType := runtime.GOOS; osType == "windows" { 136 file = strings.ReplaceAll(file, "\\", "/") 137 } 138 idx := strings.LastIndex(file, "/") 139 file = file[idx+1:] 140 return 141 } 142 143 // toSafeJSONString converts to valid JSON string, as the original string may contain '\\', '\n', '\r', '\t' and so on. 144 func toSafeJSONString(s string) []byte { 145 if data, err := json.Marshal(json.RawMessage(s)); err == nil { 146 return data 147 } else { 148 return []byte("\"" + s + "\"") 149 } 150 } 151 152 func AssembleMsg(depth int, logLevel, msg string, err error, keysAndValues ...interface{}) string { 153 sb := strings.Builder{} 154 sb.Grow(defaultLogMsgBufferSize) 155 156 file, line := caller(depth) 157 timeStr := time.Now().Format("2006-01-02 15:04:05.520") 158 caller := fmt.Sprintf("%s:%d", file, line) 159 sb.WriteString("{") 160 161 sb.WriteByte('"') 162 sb.WriteString("timestamp") 163 sb.WriteByte('"') 164 sb.WriteByte(':') 165 sb.WriteByte('"') 166 sb.WriteString(timeStr) 167 sb.WriteByte('"') 168 sb.WriteByte(',') 169 170 sb.WriteByte('"') 171 sb.WriteString("caller") 172 sb.WriteByte('"') 173 sb.WriteByte(':') 174 sb.WriteByte('"') 175 sb.WriteString(caller) 176 sb.WriteByte('"') 177 sb.WriteByte(',') 178 179 sb.WriteByte('"') 180 sb.WriteString("logLevel") 181 sb.WriteByte('"') 182 sb.WriteByte(':') 183 sb.WriteByte('"') 184 sb.WriteString(logLevel) 185 sb.WriteByte('"') 186 sb.WriteByte(',') 187 188 sb.WriteByte('"') 189 sb.WriteString("msg") 190 sb.WriteByte('"') 191 sb.WriteByte(':') 192 data := toSafeJSONString(msg) 193 sb.Write(data) 194 195 kvLen := len(keysAndValues) 196 if kvLen&1 != 0 { 197 sb.WriteByte(',') 198 sb.WriteByte('"') 199 sb.WriteString("kvs") 200 sb.WriteByte('"') 201 sb.WriteByte(':') 202 s := fmt.Sprintf("%+v", keysAndValues) 203 data := toSafeJSONString(s) 204 sb.Write(data) 205 } else if kvLen != 0 { 206 for i := 0; i < kvLen; { 207 k := keysAndValues[i] 208 v := keysAndValues[i+1] 209 kStr, kIsStr := k.(string) 210 if !kIsStr { 211 kStr = fmt.Sprintf("%+v", k) 212 } 213 sb.WriteByte(',') 214 data := toSafeJSONString(kStr) 215 sb.Write(data) 216 sb.WriteByte(':') 217 switch v.(type) { 218 case string: 219 data := toSafeJSONString(v.(string)) 220 sb.Write(data) 221 case error: 222 data := toSafeJSONString(v.(error).Error()) 223 sb.Write(data) 224 default: 225 if vbs, err := json.Marshal(v); err != nil { 226 s := fmt.Sprintf("%+v", v) 227 data := toSafeJSONString(s) 228 sb.Write(data) 229 } else { 230 sb.Write(vbs) 231 } 232 } 233 i = i + 2 234 } 235 } 236 sb.WriteByte('}') 237 if err != nil { 238 sb.WriteString("\n") 239 sb.WriteString(fmt.Sprintf("%+v", err)) 240 } 241 return sb.String() 242 } 243 244 func (l *DefaultLogger) Debug(msg string, keysAndValues ...interface{}) { 245 if !l.DebugEnabled() { 246 return 247 } 248 l.log.Print(AssembleMsg(GlobalCallerDepth, "DEBUG", msg, nil, keysAndValues...)) 249 } 250 251 func (l *DefaultLogger) DebugEnabled() bool { 252 return DebugLevel >= globalLogLevel 253 } 254 255 func (l *DefaultLogger) Info(msg string, keysAndValues ...interface{}) { 256 if !l.InfoEnabled() { 257 return 258 } 259 l.log.Print(AssembleMsg(GlobalCallerDepth, "INFO", msg, nil, keysAndValues...)) 260 } 261 262 func (l *DefaultLogger) InfoEnabled() bool { 263 return InfoLevel >= globalLogLevel 264 } 265 266 func (l *DefaultLogger) Warn(msg string, keysAndValues ...interface{}) { 267 if !l.WarnEnabled() { 268 return 269 } 270 271 l.log.Print(AssembleMsg(GlobalCallerDepth, "WARNING", msg, nil, keysAndValues...)) 272 } 273 274 func (l *DefaultLogger) WarnEnabled() bool { 275 return WarnLevel >= globalLogLevel 276 } 277 278 func (l *DefaultLogger) Error(err error, msg string, keysAndValues ...interface{}) { 279 if !l.ErrorEnabled() { 280 return 281 } 282 l.log.Print(AssembleMsg(GlobalCallerDepth, "ERROR", msg, err, keysAndValues...)) 283 } 284 285 func (l *DefaultLogger) ErrorEnabled() bool { 286 return ErrorLevel >= globalLogLevel 287 } 288 289 func Debug(msg string, keysAndValues ...interface{}) { 290 globalLogger.Debug(msg, keysAndValues...) 291 } 292 293 func DebugEnabled() bool { 294 return globalLogger.DebugEnabled() 295 } 296 297 func Info(msg string, keysAndValues ...interface{}) { 298 globalLogger.Info(msg, keysAndValues...) 299 } 300 301 func InfoEnabled() bool { 302 return globalLogger.InfoEnabled() 303 } 304 305 func Warn(msg string, keysAndValues ...interface{}) { 306 globalLogger.Warn(msg, keysAndValues...) 307 } 308 309 func WarnEnabled() bool { 310 return globalLogger.WarnEnabled() 311 } 312 313 func Error(err error, msg string, keysAndValues ...interface{}) { 314 globalLogger.Error(err, msg, keysAndValues...) 315 } 316 317 func ErrorEnabled() bool { 318 return globalLogger.ErrorEnabled() 319 }