github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/log/logger.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 8 "github.com/go-stack/stack" 9 ) 10 11 const timeKey = "t" 12 const lvlKey = "lvl" 13 const msgKey = "msg" 14 const ctxKey = "ctx" 15 const errorKey = "LOG15_ERROR" 16 const skipLevel = 2 17 18 type Lvl int 19 20 const ( 21 LvlCrit Lvl = iota 22 LvlError 23 LvlWarn 24 LvlInfo 25 LvlDebug 26 LvlTrace 27 LvlDiscard Lvl = -1 28 ) 29 30 // AlignedString returns a 5-character string containing the name of a Lvl. 31 func (l Lvl) AlignedString() string { 32 switch l { 33 case LvlTrace: 34 return "TRACE" 35 case LvlDebug: 36 return "DEBUG" 37 case LvlInfo: 38 return "INFO " 39 case LvlWarn: 40 return "WARN " 41 case LvlError: 42 return "ERROR" 43 case LvlCrit: 44 return "CRIT " 45 default: 46 panic("bad level") 47 } 48 } 49 50 // Strings returns the name of a Lvl. 51 func (l Lvl) String() string { 52 switch l { 53 case LvlTrace: 54 return "trce" 55 case LvlDebug: 56 return "dbug" 57 case LvlInfo: 58 return "info" 59 case LvlWarn: 60 return "warn" 61 case LvlError: 62 return "eror" 63 case LvlCrit: 64 return "crit" 65 default: 66 panic("bad level") 67 } 68 } 69 70 // LvlFromString returns the appropriate Lvl from a string name. 71 // Useful for parsing command line args and configuration files. 72 func LvlFromString(lvlString string) (Lvl, error) { 73 switch lvlString { 74 case "trace", "trce": 75 return LvlTrace, nil 76 case "debug", "dbug": 77 return LvlDebug, nil 78 case "info": 79 return LvlInfo, nil 80 case "warn": 81 return LvlWarn, nil 82 case "error", "eror": 83 return LvlError, nil 84 case "crit": 85 return LvlCrit, nil 86 default: 87 return LvlDebug, fmt.Errorf("unknown level: %v", lvlString) 88 } 89 } 90 91 // A Record is what a Logger asks its handler to write 92 type Record struct { 93 Time time.Time 94 Lvl Lvl 95 Msg string 96 Ctx []interface{} 97 Call stack.Call 98 KeyNames RecordKeyNames 99 } 100 101 // RecordKeyNames gets stored in a Record when the write function is executed. 102 type RecordKeyNames struct { 103 Time string 104 Msg string 105 Lvl string 106 Ctx string 107 } 108 109 type Logging func(msg string, ctx ...interface{}) 110 111 // A Logger writes key/value pairs to a Handler 112 type Logger interface { 113 // New returns a new Logger that has this logger's context plus the given context 114 New(ctx ...interface{}) Logger 115 116 // GetHandler gets the handler associated with the logger. 117 GetHandler() Handler 118 119 // SetHandler updates the logger to write records to the specified handler. 120 SetHandler(h Handler) 121 122 // Log a message at the given level with context key/value pairs 123 Trace(msg string, ctx ...interface{}) 124 Debug(msg string, ctx ...interface{}) 125 Info(msg string, ctx ...interface{}) 126 Warn(msg string, ctx ...interface{}) 127 Error(msg string, ctx ...interface{}) 128 Crit(msg string, ctx ...interface{}) 129 130 OnTrace(func(l Logging)) 131 OnDebug(func(l Logging)) 132 OnInfo(func(l Logging)) 133 OnWarn(func(l Logging)) 134 OnError(func(l Logging)) 135 OnCrit(func(l Logging)) 136 } 137 138 type logger struct { 139 ctx []interface{} 140 h *swapHandler 141 } 142 143 func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) { 144 if l.h.Level() < lvl { 145 return 146 } 147 148 l.h.Log(&Record{ 149 Time: time.Now(), 150 Lvl: lvl, 151 Msg: msg, 152 Ctx: newContext(l.ctx, ctx), 153 Call: stack.Caller(skip), 154 KeyNames: RecordKeyNames{ 155 Time: timeKey, 156 Msg: msgKey, 157 Lvl: lvlKey, 158 Ctx: ctxKey, 159 }, 160 }) 161 } 162 163 func (l *logger) New(ctx ...interface{}) Logger { 164 child := &logger{newContext(l.ctx, ctx), new(swapHandler)} 165 child.SetHandler(l.h) 166 return child 167 } 168 169 func newContext(prefix []interface{}, suffix []interface{}) []interface{} { 170 normalizedSuffix := normalize(suffix) 171 newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) 172 n := copy(newCtx, prefix) 173 copy(newCtx[n:], normalizedSuffix) 174 return newCtx 175 } 176 177 func (l *logger) Trace(msg string, ctx ...interface{}) { 178 l.write(msg, LvlTrace, ctx, skipLevel) 179 } 180 181 func (l *logger) Debug(msg string, ctx ...interface{}) { 182 l.write(msg, LvlDebug, ctx, skipLevel) 183 } 184 185 func (l *logger) Info(msg string, ctx ...interface{}) { 186 l.write(msg, LvlInfo, ctx, skipLevel) 187 } 188 189 func (l *logger) Warn(msg string, ctx ...interface{}) { 190 l.write(msg, LvlWarn, ctx, skipLevel) 191 } 192 193 func (l *logger) Error(msg string, ctx ...interface{}) { 194 l.write(msg, LvlError, ctx, skipLevel) 195 } 196 197 func (l *logger) Crit(msg string, ctx ...interface{}) { 198 l.write(msg, LvlCrit, ctx, skipLevel) 199 os.Exit(1) 200 } 201 202 func (l *logger) GetHandler() Handler { 203 return l.h.Get() 204 } 205 206 func (l *logger) SetHandler(h Handler) { 207 l.h.Swap(h) 208 } 209 210 func (l *logger) OnTrace(fn func(l Logging)) { 211 if l.GetHandler().Level() >= LvlTrace { 212 fn(l.Trace) 213 } 214 } 215 216 func (l *logger) OnDebug(fn func(l Logging)) { 217 if l.GetHandler().Level() >= LvlDebug { 218 fn(l.Debug) 219 } 220 } 221 func (l *logger) OnInfo(fn func(l Logging)) { 222 if l.GetHandler().Level() >= LvlInfo { 223 fn(l.Info) 224 } 225 } 226 func (l *logger) OnWarn(fn func(l Logging)) { 227 if l.GetHandler().Level() >= LvlWarn { 228 fn(l.Warn) 229 } 230 } 231 func (l *logger) OnError(fn func(l Logging)) { 232 if l.GetHandler().Level() >= LvlError { 233 fn(l.Error) 234 } 235 } 236 func (l *logger) OnCrit(fn func(l Logging)) { 237 if l.GetHandler().Level() >= LvlCrit { 238 fn(l.Crit) 239 } 240 } 241 242 func normalize(ctx []interface{}) []interface{} { 243 // if the caller passed a Ctx object, then expand it 244 if len(ctx) == 1 { 245 if ctxMap, ok := ctx[0].(Ctx); ok { 246 ctx = ctxMap.toArray() 247 } 248 } 249 250 // ctx needs to be even because it's a series of key/value pairs 251 // no one wants to check for errors on logging functions, 252 // so instead of erroring on bad input, we'll just make sure 253 // that things are the right length and users can fix bugs 254 // when they see the output looks wrong 255 if len(ctx)%2 != 0 { 256 ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") 257 } 258 259 return ctx 260 } 261 262 // Lazy allows you to defer calculation of a logged value that is expensive 263 // to compute until it is certain that it must be evaluated with the given filters. 264 // 265 // Lazy may also be used in conjunction with a Logger's New() function 266 // to generate a child logger which always reports the current value of changing 267 // state. 268 // 269 // You may wrap any function which takes no arguments to Lazy. It may return any 270 // number of values of any type. 271 type Lazy struct { 272 Fn interface{} 273 } 274 275 // Ctx is a map of key/value pairs to pass as context to a log function 276 // Use this only if you really need greater safety around the arguments you pass 277 // to the logging functions. 278 type Ctx map[string]interface{} 279 280 func (c Ctx) toArray() []interface{} { 281 arr := make([]interface{}, len(c)*2) 282 283 i := 0 284 for k, v := range c { 285 arr[i] = k 286 arr[i+1] = v 287 i += 2 288 } 289 290 return arr 291 }