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