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