github.com/MikyChow/arbitrum-go-ethereum@v0.0.0-20230306102812-078da49636de/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 128 type logger struct { 129 ctx []interface{} 130 h *swapHandler 131 } 132 133 func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) { 134 l.h.Log(&Record{ 135 Time: time.Now(), 136 Lvl: lvl, 137 Msg: msg, 138 Ctx: newContext(l.ctx, ctx), 139 Call: stack.Caller(skip), 140 KeyNames: RecordKeyNames{ 141 Time: timeKey, 142 Msg: msgKey, 143 Lvl: lvlKey, 144 Ctx: ctxKey, 145 }, 146 }) 147 } 148 149 func (l *logger) New(ctx ...interface{}) Logger { 150 child := &logger{newContext(l.ctx, ctx), new(swapHandler)} 151 child.SetHandler(l.h) 152 return child 153 } 154 155 func newContext(prefix []interface{}, suffix []interface{}) []interface{} { 156 normalizedSuffix := normalize(suffix) 157 newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) 158 n := copy(newCtx, prefix) 159 copy(newCtx[n:], normalizedSuffix) 160 return newCtx 161 } 162 163 func (l *logger) Trace(msg string, ctx ...interface{}) { 164 l.write(msg, LvlTrace, ctx, skipLevel) 165 } 166 167 func (l *logger) Debug(msg string, ctx ...interface{}) { 168 l.write(msg, LvlDebug, ctx, skipLevel) 169 } 170 171 func (l *logger) Info(msg string, ctx ...interface{}) { 172 l.write(msg, LvlInfo, ctx, skipLevel) 173 } 174 175 func (l *logger) Warn(msg string, ctx ...interface{}) { 176 l.write(msg, LvlWarn, ctx, skipLevel) 177 } 178 179 func (l *logger) Error(msg string, ctx ...interface{}) { 180 l.write(msg, LvlError, ctx, skipLevel) 181 } 182 183 func (l *logger) Crit(msg string, ctx ...interface{}) { 184 l.write(msg, LvlCrit, ctx, skipLevel) 185 os.Exit(1) 186 } 187 188 func (l *logger) GetHandler() Handler { 189 return l.h.Get() 190 } 191 192 func (l *logger) SetHandler(h Handler) { 193 l.h.Swap(h) 194 } 195 196 func normalize(ctx []interface{}) []interface{} { 197 // if the caller passed a Ctx object, then expand it 198 if len(ctx) == 1 { 199 if ctxMap, ok := ctx[0].(Ctx); ok { 200 ctx = ctxMap.toArray() 201 } 202 } 203 204 // ctx needs to be even because it's a series of key/value pairs 205 // no one wants to check for errors on logging functions, 206 // so instead of erroring on bad input, we'll just make sure 207 // that things are the right length and users can fix bugs 208 // when they see the output looks wrong 209 if len(ctx)%2 != 0 { 210 ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") 211 } 212 213 return ctx 214 } 215 216 // Lazy allows you to defer calculation of a logged value that is expensive 217 // to compute until it is certain that it must be evaluated with the given filters. 218 // 219 // Lazy may also be used in conjunction with a Logger's New() function 220 // to generate a child logger which always reports the current value of changing 221 // state. 222 // 223 // You may wrap any function which takes no arguments to Lazy. It may return any 224 // number of values of any type. 225 type Lazy struct { 226 Fn interface{} 227 } 228 229 // Ctx is a map of key/value pairs to pass as context to a log function 230 // Use this only if you really need greater safety around the arguments you pass 231 // to the logging functions. 232 type Ctx map[string]interface{} 233 234 func (c Ctx) toArray() []interface{} { 235 arr := make([]interface{}, len(c)*2) 236 237 i := 0 238 for k, v := range c { 239 arr[i] = k 240 arr[i+1] = v 241 i += 2 242 } 243 244 return arr 245 }