bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/slog/slog.go (about) 1 // Package slog provides a cross-platform logging interface. It is designed to 2 // provide a universal logging interface on any operating system. It defaults to 3 // using the log package of the standard library, but can easily be used with 4 // other logging backends. Thus, we can use syslog on unicies and the event log 5 // on windows. 6 package slog // import "bosun.org/slog" 7 8 import ( 9 "fmt" 10 "log" 11 "os" 12 "path/filepath" 13 "runtime" 14 "runtime/debug" 15 "strings" 16 ) 17 18 var ( 19 // LogLineNumber prints the file and line number of the caller. 20 LogLineNumber = true 21 ) 22 23 // Logger is the slog logging interface. 24 type Logger interface { 25 Error(v string) 26 Info(v string) 27 Warning(v string) 28 Fatal(v string) 29 } 30 31 // StdLog logs to a log.Logger. 32 type StdLog struct { 33 Log *log.Logger 34 } 35 36 // Fatal logs a fatal message and calls os.Exit(1). 37 func (s *StdLog) Fatal(v string) { 38 s.Log.Fatalln("fatal:", rmNl(v)) 39 } 40 41 // Error logs an error message. 42 func (s *StdLog) Error(v string) { 43 s.Log.Println("error:", rmNl(v)) 44 } 45 46 // Info logs an info message. 47 func (s *StdLog) Info(v string) { 48 s.Log.Println("info:", rmNl(v)) 49 } 50 51 // Warning logs a warning message. 52 func (s *StdLog) Warning(v string) { 53 s.Log.Println("warning:", rmNl(v)) 54 } 55 56 func rmNl(v string) string { 57 if strings.HasSuffix(v, "\n") { 58 v = v[:len(v)-1] 59 } 60 return v 61 } 62 63 var logging Logger = &StdLog{Log: log.New(os.Stderr, "", log.LstdFlags)} 64 65 // Set configures l to be the default logger for slog. 66 func Set(l Logger) { 67 logging = l 68 } 69 70 // Info logs an info message. 71 func Info(v ...interface{}) { 72 output(logging.Info, v...) 73 } 74 75 // Infof logs an info message. 76 func Infof(format string, v ...interface{}) { 77 outputf(logging.Info, format, v...) 78 } 79 80 // Infoln logs an info message. 81 func Infoln(v ...interface{}) { 82 outputln(logging.Info, v...) 83 } 84 85 // Warning logs a warning message. 86 func Warning(v ...interface{}) { 87 output(logging.Warning, v...) 88 } 89 90 // Warningf logs a warning message. 91 func Warningf(format string, v ...interface{}) { 92 outputf(logging.Warning, format, v...) 93 } 94 95 // Warningln logs a warning message. 96 func Warningln(v ...interface{}) { 97 outputln(logging.Warning, v...) 98 } 99 100 // Error logs an error message. 101 func Error(v ...interface{}) { 102 output(logging.Error, v...) 103 } 104 105 // Errorf logs an error message. 106 func Errorf(format string, v ...interface{}) { 107 outputf(logging.Error, format, v...) 108 } 109 110 // Errorln logs an error message. 111 func Errorln(v ...interface{}) { 112 outputln(logging.Error, v...) 113 } 114 115 // Fatal logs a fatal message and calls os.Exit(1). 116 func Fatal(v ...interface{}) { 117 output(logging.Fatal, v...) 118 // Call os.Exit here just in case the logging package we are using doesn't. 119 os.Exit(1) 120 } 121 122 // Fatalf logs a fatal message and calls os.Exit(1). 123 func Fatalf(format string, v ...interface{}) { 124 outputf(logging.Fatal, format, v...) 125 os.Exit(1) 126 } 127 128 // Fatalln logs a fatal message and calls os.Exit(1). 129 func Fatalln(v ...interface{}) { 130 outputln(logging.Fatal, v...) 131 os.Exit(1) 132 } 133 134 func out(f func(string), s string) { 135 if LogLineNumber { 136 if _, filename, line, ok := runtime.Caller(3); ok { 137 s = fmt.Sprintf("%s:%d: %v", filepath.Base(filename), line, s) 138 } 139 } 140 f(s) 141 } 142 143 func output(f func(string), v ...interface{}) { 144 out(f, fmt.Sprint(v...)) 145 } 146 147 func outputf(f func(string), format string, v ...interface{}) { 148 out(f, fmt.Sprintf(format, v...)) 149 } 150 151 func outputln(f func(string), v ...interface{}) { 152 out(f, fmt.Sprintln(v...)) 153 } 154 155 type wrappedError struct { 156 error 157 line string 158 } 159 160 func (w wrappedError) Error() string { 161 return fmt.Sprintf("%s: %s", w.line, w.error.Error()) 162 } 163 164 //Helper to wrap an error with relevant line information. Wrap an error when it enters "our" code before passing it up the stack. 165 //This will help narrow down the source of the error. 166 func Wrap(err error) error { 167 if err == nil { 168 return nil 169 } 170 if _, ok := err.(wrappedError); ok { 171 return err 172 } 173 line := "" 174 if _, filename, l, ok := runtime.Caller(1); ok { 175 line = fmt.Sprintf("%s:%d", filepath.Base(filename), l) 176 } else { 177 return err 178 } 179 return wrappedError{err, line} 180 } 181 182 //Add defer slog.PanicAsFatal() to the top of a goroutine to recover panics and log them using slog.Fatal 183 func PanicAsFatal() { 184 if r := recover(); r != nil { 185 s := string(debug.Stack()[:]) 186 Fatalf("panic: %s\nStackTrace:\n\n%s", r, s) 187 } 188 }