github.com/lingyao2333/mo-zero@v1.4.1/core/logx/logs.go (about) 1 package logx 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "path" 9 "runtime/debug" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/lingyao2333/mo-zero/core/sysx" 15 ) 16 17 const callerDepth = 4 18 19 var ( 20 timeFormat = "2006-01-02T15:04:05.000Z07:00" 21 logLevel uint32 22 encoding uint32 = jsonEncodingType 23 // use uint32 for atomic operations 24 disableLog uint32 25 disableStat uint32 26 options logOptions 27 writer = new(atomicWriter) 28 setupOnce sync.Once 29 ) 30 31 type ( 32 // LogField is a key-value pair that will be added to the log entry. 33 LogField struct { 34 Key string 35 Value interface{} 36 } 37 38 // LogOption defines the method to customize the logging. 39 LogOption func(options *logOptions) 40 41 logEntry map[string]interface{} 42 43 logOptions struct { 44 gzipEnabled bool 45 logStackCooldownMills int 46 keepDays int 47 maxBackups int 48 maxSize int 49 rotationRule string 50 } 51 ) 52 53 // Alert alerts v in alert level, and the message is written to error log. 54 func Alert(v string) { 55 getWriter().Alert(v) 56 } 57 58 // Close closes the logging. 59 func Close() error { 60 if w := writer.Swap(nil); w != nil { 61 return w.(io.Closer).Close() 62 } 63 64 return nil 65 } 66 67 // Debug writes v into access log. 68 func Debug(v ...interface{}) { 69 writeDebug(fmt.Sprint(v...)) 70 } 71 72 // Debugf writes v with format into access log. 73 func Debugf(format string, v ...interface{}) { 74 writeDebug(fmt.Sprintf(format, v...)) 75 } 76 77 // Debugv writes v into access log with json content. 78 func Debugv(v interface{}) { 79 writeDebug(v) 80 } 81 82 // Debugw writes msg along with fields into access log. 83 func Debugw(msg string, fields ...LogField) { 84 writeDebug(msg, fields...) 85 } 86 87 // Disable disables the logging. 88 func Disable() { 89 atomic.StoreUint32(&disableLog, 1) 90 writer.Store(nopWriter{}) 91 } 92 93 // DisableStat disables the stat logs. 94 func DisableStat() { 95 atomic.StoreUint32(&disableStat, 1) 96 } 97 98 // Error writes v into error log. 99 func Error(v ...interface{}) { 100 writeError(fmt.Sprint(v...)) 101 } 102 103 // Errorf writes v with format into error log. 104 func Errorf(format string, v ...interface{}) { 105 writeError(fmt.Errorf(format, v...).Error()) 106 } 107 108 // ErrorStack writes v along with call stack into error log. 109 func ErrorStack(v ...interface{}) { 110 // there is newline in stack string 111 writeStack(fmt.Sprint(v...)) 112 } 113 114 // ErrorStackf writes v along with call stack in format into error log. 115 func ErrorStackf(format string, v ...interface{}) { 116 // there is newline in stack string 117 writeStack(fmt.Sprintf(format, v...)) 118 } 119 120 // Errorv writes v into error log with json content. 121 // No call stack attached, because not elegant to pack the messages. 122 func Errorv(v interface{}) { 123 writeError(v) 124 } 125 126 // Errorw writes msg along with fields into error log. 127 func Errorw(msg string, fields ...LogField) { 128 writeError(msg, fields...) 129 } 130 131 // Field returns a LogField for the given key and value. 132 func Field(key string, value interface{}) LogField { 133 switch val := value.(type) { 134 case error: 135 return LogField{Key: key, Value: val.Error()} 136 case []error: 137 var errs []string 138 for _, err := range val { 139 errs = append(errs, err.Error()) 140 } 141 return LogField{Key: key, Value: errs} 142 case time.Duration: 143 return LogField{Key: key, Value: fmt.Sprint(val)} 144 case []time.Duration: 145 var durs []string 146 for _, dur := range val { 147 durs = append(durs, fmt.Sprint(dur)) 148 } 149 return LogField{Key: key, Value: durs} 150 case []time.Time: 151 var times []string 152 for _, t := range val { 153 times = append(times, fmt.Sprint(t)) 154 } 155 return LogField{Key: key, Value: times} 156 case fmt.Stringer: 157 return LogField{Key: key, Value: val.String()} 158 case []fmt.Stringer: 159 var strs []string 160 for _, str := range val { 161 strs = append(strs, str.String()) 162 } 163 return LogField{Key: key, Value: strs} 164 default: 165 return LogField{Key: key, Value: val} 166 } 167 } 168 169 // Info writes v into access log. 170 func Info(v ...interface{}) { 171 writeInfo(fmt.Sprint(v...)) 172 } 173 174 // Infof writes v with format into access log. 175 func Infof(format string, v ...interface{}) { 176 writeInfo(fmt.Sprintf(format, v...)) 177 } 178 179 // Infov writes v into access log with json content. 180 func Infov(v interface{}) { 181 writeInfo(v) 182 } 183 184 // Infow writes msg along with fields into access log. 185 func Infow(msg string, fields ...LogField) { 186 writeInfo(msg, fields...) 187 } 188 189 // Must checks if err is nil, otherwise logs the error and exits. 190 func Must(err error) { 191 if err == nil { 192 return 193 } 194 195 msg := err.Error() 196 log.Print(msg) 197 getWriter().Severe(msg) 198 os.Exit(1) 199 } 200 201 // MustSetup sets up logging with given config c. It exits on error. 202 func MustSetup(c LogConf) { 203 Must(SetUp(c)) 204 } 205 206 // Reset clears the writer and resets the log level. 207 func Reset() Writer { 208 return writer.Swap(nil) 209 } 210 211 // SetLevel sets the logging level. It can be used to suppress some logs. 212 func SetLevel(level uint32) { 213 atomic.StoreUint32(&logLevel, level) 214 } 215 216 // SetWriter sets the logging writer. It can be used to customize the logging. 217 func SetWriter(w Writer) { 218 if atomic.LoadUint32(&disableLog) == 0 { 219 writer.Store(w) 220 } 221 } 222 223 // SetUp sets up the logx. If already set up, just return nil. 224 // we allow SetUp to be called multiple times, because for example 225 // we need to allow different service frameworks to initialize logx respectively. 226 func SetUp(c LogConf) (err error) { 227 // Just ignore the subsequent SetUp calls. 228 // Because multiple services in one process might call SetUp respectively. 229 // Need to wait for the first caller to complete the execution. 230 setupOnce.Do(func() { 231 setupLogLevel(c) 232 233 if len(c.TimeFormat) > 0 { 234 timeFormat = c.TimeFormat 235 } 236 237 switch c.Encoding { 238 case plainEncoding: 239 atomic.StoreUint32(&encoding, plainEncodingType) 240 default: 241 atomic.StoreUint32(&encoding, jsonEncodingType) 242 } 243 244 switch c.Mode { 245 case fileMode: 246 err = setupWithFiles(c) 247 case volumeMode: 248 err = setupWithVolume(c) 249 default: 250 setupWithConsole() 251 } 252 }) 253 254 return 255 } 256 257 // Severe writes v into severe log. 258 func Severe(v ...interface{}) { 259 writeSevere(fmt.Sprint(v...)) 260 } 261 262 // Severef writes v with format into severe log. 263 func Severef(format string, v ...interface{}) { 264 writeSevere(fmt.Sprintf(format, v...)) 265 } 266 267 // Slow writes v into slow log. 268 func Slow(v ...interface{}) { 269 writeSlow(fmt.Sprint(v...)) 270 } 271 272 // Slowf writes v with format into slow log. 273 func Slowf(format string, v ...interface{}) { 274 writeSlow(fmt.Sprintf(format, v...)) 275 } 276 277 // Slowv writes v into slow log with json content. 278 func Slowv(v interface{}) { 279 writeSlow(v) 280 } 281 282 // Sloww writes msg along with fields into slow log. 283 func Sloww(msg string, fields ...LogField) { 284 writeSlow(msg, fields...) 285 } 286 287 // Stat writes v into stat log. 288 func Stat(v ...interface{}) { 289 writeStat(fmt.Sprint(v...)) 290 } 291 292 // Statf writes v with format into stat log. 293 func Statf(format string, v ...interface{}) { 294 writeStat(fmt.Sprintf(format, v...)) 295 } 296 297 // WithCooldownMillis customizes logging on writing call stack interval. 298 func WithCooldownMillis(millis int) LogOption { 299 return func(opts *logOptions) { 300 opts.logStackCooldownMills = millis 301 } 302 } 303 304 // WithKeepDays customizes logging to keep logs with days. 305 func WithKeepDays(days int) LogOption { 306 return func(opts *logOptions) { 307 opts.keepDays = days 308 } 309 } 310 311 // WithGzip customizes logging to automatically gzip the log files. 312 func WithGzip() LogOption { 313 return func(opts *logOptions) { 314 opts.gzipEnabled = true 315 } 316 } 317 318 // WithMaxBackups customizes how many log files backups will be kept. 319 func WithMaxBackups(count int) LogOption { 320 return func(opts *logOptions) { 321 opts.maxBackups = count 322 } 323 } 324 325 // WithMaxSize customizes how much space the writing log file can take up. 326 func WithMaxSize(size int) LogOption { 327 return func(opts *logOptions) { 328 opts.maxSize = size 329 } 330 } 331 332 // WithRotation customizes which log rotation rule to use. 333 func WithRotation(r string) LogOption { 334 return func(opts *logOptions) { 335 opts.rotationRule = r 336 } 337 } 338 339 func addCaller(fields ...LogField) []LogField { 340 return append(fields, Field(callerKey, getCaller(callerDepth))) 341 } 342 343 func createOutput(path string) (io.WriteCloser, error) { 344 if len(path) == 0 { 345 return nil, ErrLogPathNotSet 346 } 347 348 switch options.rotationRule { 349 case sizeRotationRule: 350 return NewLogger(path, NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays, 351 options.maxSize, options.maxBackups, options.gzipEnabled), options.gzipEnabled) 352 default: 353 return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays, 354 options.gzipEnabled), options.gzipEnabled) 355 } 356 } 357 358 func getWriter() Writer { 359 w := writer.Load() 360 if w == nil { 361 w = writer.StoreIfNil(newConsoleWriter()) 362 } 363 364 return w 365 } 366 367 func handleOptions(opts []LogOption) { 368 for _, opt := range opts { 369 opt(&options) 370 } 371 } 372 373 func setupLogLevel(c LogConf) { 374 switch c.Level { 375 case levelDebug: 376 SetLevel(DebugLevel) 377 case levelInfo: 378 SetLevel(InfoLevel) 379 case levelError: 380 SetLevel(ErrorLevel) 381 case levelSevere: 382 SetLevel(SevereLevel) 383 } 384 } 385 386 func setupWithConsole() { 387 SetWriter(newConsoleWriter()) 388 } 389 390 func setupWithFiles(c LogConf) error { 391 w, err := newFileWriter(c) 392 if err != nil { 393 return err 394 } 395 396 SetWriter(w) 397 return nil 398 } 399 400 func setupWithVolume(c LogConf) error { 401 if len(c.ServiceName) == 0 { 402 return ErrLogServiceNameNotSet 403 } 404 405 c.Path = path.Join(c.Path, c.ServiceName, sysx.Hostname()) 406 return setupWithFiles(c) 407 } 408 409 func shallLog(level uint32) bool { 410 return atomic.LoadUint32(&logLevel) <= level 411 } 412 413 func shallLogStat() bool { 414 return atomic.LoadUint32(&disableStat) == 0 415 } 416 417 func writeDebug(val interface{}, fields ...LogField) { 418 if shallLog(DebugLevel) { 419 getWriter().Debug(val, addCaller(fields...)...) 420 } 421 } 422 423 func writeError(val interface{}, fields ...LogField) { 424 if shallLog(ErrorLevel) { 425 getWriter().Error(val, addCaller(fields...)...) 426 } 427 } 428 429 func writeInfo(val interface{}, fields ...LogField) { 430 if shallLog(InfoLevel) { 431 getWriter().Info(val, addCaller(fields...)...) 432 } 433 } 434 435 func writeSevere(msg string) { 436 if shallLog(SevereLevel) { 437 getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack()))) 438 } 439 } 440 441 func writeSlow(val interface{}, fields ...LogField) { 442 if shallLog(ErrorLevel) { 443 getWriter().Slow(val, addCaller(fields...)...) 444 } 445 } 446 447 func writeStack(msg string) { 448 if shallLog(ErrorLevel) { 449 getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack()))) 450 } 451 } 452 453 func writeStat(msg string) { 454 if shallLogStat() && shallLog(InfoLevel) { 455 getWriter().Stat(msg, addCaller()...) 456 } 457 }