log/log.go (96 lines of code) (raw):

// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package log import ( "fmt" "math/rand" "os" "strings" ) //Log levels const ( Panic = iota Fatal Error Warn Info Debug ) var levels = map[string]int{"panic": Panic + 1, "fatal": Fatal + 1, "error": Error + 1, "warn": Warn + 1, "info": Info + 1, "debug": Debug + 1} //LoggerConstructor initializes logger plugin type LoggerConstructor func(level int, production bool) (Logger, error) //loggers contains registered logger plugins var loggers map[string]LoggerConstructor func registerPlugin(name string, init LoggerConstructor) { if loggers == nil { loggers = make(map[string]LoggerConstructor) } loggers[name] = init } //def is a instance of default logger var def = newStd(Info) //Logger is our contract for the logger type Logger interface { // Log at debug level with fmt.Printf-like formatting Debugf(format string, args ...interface{}) // Log at info level with fmt.Printf-like formatting Infof(format string, args ...interface{}) // Log at warning level with fmt.Printf-like formatting Warnf(format string, args ...interface{}) // Log at error level with fmt.Printf-like formatting Errorf(format string, args ...interface{}) // Log at fatal level with fmt.Printf-like formatting, then terminate process (irrecoverable) Fatalf(format string, args ...interface{}) // Log at panic level with fmt.Printf-like formatting, then panic (recoverable) Panicf(format string, args ...interface{}) // Return a logger with the specified key-value pairs set, to be included in a subsequent normal logging call WithFields(keyValues Fields) Logger } //Fields is a map of key/value pairs which can be attached to a logger //This fields will be printed with every message of the logger type Fields map[string]interface{} //ParseLevel converts string level to integer const func ParseLevel(s string) int { if levels[strings.ToLower(s)] != 0 { return levels[s] - 1 } return Info } //Configure initializes default logger func Configure(logType string, logLevel string, production bool) { configure := loggers[strings.ToLower(logType)] if configure == nil { fmt.Printf("Logger '%v' is not registered, falling back to 'std'\n", logType) configure = configureStd } var err error def, err = configure(ParseLevel(logLevel), production) if err != nil { fmt.Printf("Error configuring logger: %v\n", err) os.Exit(1) } Debugf("Configured logger: %v, level: %v", logType, logLevel) } /* func parentFileLineFunc(skip int) (string, int, string) { pc, file, no, _ := runtime.Caller(2 + skip) details := runtime.FuncForPC(pc) return path.Base(file), no, path.Base(details.Name()) } func el(l Logger, err error, skip int) bool { if err != nil { file, no, fn := parentFileLineFunc(skip) // l.Errorf("%v:%v %v: %v", file, no, fn, err.Error()) l.WithFields(Fields{"file": file, "line": no, "func": fn}).Errorf("%v", err.Error()) return true } return false } */ //E logs the error if it's not nil //Return true if error is not nil func E(err error) bool { if err != nil { def.Errorf(err.Error()) } return err != nil } //F logs the error if it's not nil and terminate the process func F(err error) { if err != nil { def.Fatalf(err.Error()) } } //EL logs the error if it's not nil with given logger //Return true if error is not nil //FIXME: this prints file/line of the l.Errorf call not EL caller func EL(l Logger, err error) bool { if err != nil { l.Errorf(err.Error()) } return err != nil } //Debugf logs a message with given format and arguments func Debugf(format string, args ...interface{}) { def.Debugf(format, args...) } //Infof logs a message with given format and arguments func Infof(format string, args ...interface{}) { def.Infof(format, args...) } //Warnf logs a message with given format and arguments func Warnf(format string, args ...interface{}) { def.Warnf(format, args...) } //Errorf logs a message with given format and arguments func Errorf(format string, args ...interface{}) { def.Errorf(format, args...) } //Fatalf logs a message with given format and arguments and then finishes the //process with error exit code func Fatalf(format string, args ...interface{}) { def.Fatalf(format, args...) } //Panicf logs a message with given format and arguments and then panics func Panicf(format string, args ...interface{}) { def.Panicf(format, args...) } //WithFields attaches given key/value fields and return new logger with those //fields attached func WithFields(keyValues Fields) Logger { return def.WithFields(keyValues) } //GenWorkerID generates random 32bit worker ID to be used by loggers func GenWorkerID() string { return fmt.Sprintf("%0x", rand.Uint32()) }