github.com/theQRL/go-zond@v0.1.1/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 // String 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 trace level with context key/value pairs 120 // 121 // # Usage 122 // 123 // log.Trace("msg") 124 // log.Trace("msg", "key1", val1) 125 // log.Trace("msg", "key1", val1, "key2", val2) 126 Trace(msg string, ctx ...interface{}) 127 128 // Log a message at the debug level with context key/value pairs 129 // 130 // # Usage Examples 131 // 132 // log.Debug("msg") 133 // log.Debug("msg", "key1", val1) 134 // log.Debug("msg", "key1", val1, "key2", val2) 135 Debug(msg string, ctx ...interface{}) 136 137 // Log a message at the info level with context key/value pairs 138 // 139 // # Usage Examples 140 // 141 // log.Info("msg") 142 // log.Info("msg", "key1", val1) 143 // log.Info("msg", "key1", val1, "key2", val2) 144 Info(msg string, ctx ...interface{}) 145 146 // Log a message at the warn level with context key/value pairs 147 // 148 // # Usage Examples 149 // 150 // log.Warn("msg") 151 // log.Warn("msg", "key1", val1) 152 // log.Warn("msg", "key1", val1, "key2", val2) 153 Warn(msg string, ctx ...interface{}) 154 155 // Log a message at the error level with context key/value pairs 156 // 157 // # Usage Examples 158 // 159 // log.Error("msg") 160 // log.Error("msg", "key1", val1) 161 // log.Error("msg", "key1", val1, "key2", val2) 162 Error(msg string, ctx ...interface{}) 163 164 // Log a message at the crit level with context key/value pairs, and then exit. 165 // 166 // # Usage Examples 167 // 168 // log.Crit("msg") 169 // log.Crit("msg", "key1", val1) 170 // log.Crit("msg", "key1", val1, "key2", val2) 171 Crit(msg string, ctx ...interface{}) 172 } 173 174 type logger struct { 175 ctx []interface{} 176 h *swapHandler 177 } 178 179 func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) { 180 record := &Record{ 181 Time: time.Now(), 182 Lvl: lvl, 183 Msg: msg, 184 Ctx: newContext(l.ctx, ctx), 185 KeyNames: RecordKeyNames{ 186 Time: timeKey, 187 Msg: msgKey, 188 Lvl: lvlKey, 189 Ctx: ctxKey, 190 }, 191 } 192 if stackEnabled.Load() { 193 record.Call = stack.Caller(skip) 194 } 195 l.h.Log(record) 196 } 197 198 func (l *logger) New(ctx ...interface{}) Logger { 199 child := &logger{newContext(l.ctx, ctx), new(swapHandler)} 200 child.SetHandler(l.h) 201 return child 202 } 203 204 func newContext(prefix []interface{}, suffix []interface{}) []interface{} { 205 normalizedSuffix := normalize(suffix) 206 newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) 207 n := copy(newCtx, prefix) 208 copy(newCtx[n:], normalizedSuffix) 209 return newCtx 210 } 211 212 func (l *logger) Trace(msg string, ctx ...interface{}) { 213 l.write(msg, LvlTrace, ctx, skipLevel) 214 } 215 216 func (l *logger) Debug(msg string, ctx ...interface{}) { 217 l.write(msg, LvlDebug, ctx, skipLevel) 218 } 219 220 func (l *logger) Info(msg string, ctx ...interface{}) { 221 l.write(msg, LvlInfo, ctx, skipLevel) 222 } 223 224 func (l *logger) Warn(msg string, ctx ...interface{}) { 225 l.write(msg, LvlWarn, ctx, skipLevel) 226 } 227 228 func (l *logger) Error(msg string, ctx ...interface{}) { 229 l.write(msg, LvlError, ctx, skipLevel) 230 } 231 232 func (l *logger) Crit(msg string, ctx ...interface{}) { 233 l.write(msg, LvlCrit, ctx, skipLevel) 234 os.Exit(1) 235 } 236 237 func (l *logger) GetHandler() Handler { 238 return l.h.Get() 239 } 240 241 func (l *logger) SetHandler(h Handler) { 242 l.h.Swap(h) 243 } 244 245 func normalize(ctx []interface{}) []interface{} { 246 // if the caller passed a Ctx object, then expand it 247 if len(ctx) == 1 { 248 if ctxMap, ok := ctx[0].(Ctx); ok { 249 ctx = ctxMap.toArray() 250 } 251 } 252 253 // ctx needs to be even because it's a series of key/value pairs 254 // no one wants to check for errors on logging functions, 255 // so instead of erroring on bad input, we'll just make sure 256 // that things are the right length and users can fix bugs 257 // when they see the output looks wrong 258 if len(ctx)%2 != 0 { 259 ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") 260 } 261 262 return ctx 263 } 264 265 // Lazy allows you to defer calculation of a logged value that is expensive 266 // to compute until it is certain that it must be evaluated with the given filters. 267 // 268 // Lazy may also be used in conjunction with a Logger's New() function 269 // to generate a child logger which always reports the current value of changing 270 // state. 271 // 272 // You may wrap any function which takes no arguments to Lazy. It may return any 273 // number of values of any type. 274 type Lazy struct { 275 Fn interface{} 276 } 277 278 // Ctx is a map of key/value pairs to pass as context to a log function 279 // Use this only if you really need greater safety around the arguments you pass 280 // to the logging functions. 281 type Ctx map[string]interface{} 282 283 func (c Ctx) toArray() []interface{} { 284 arr := make([]interface{}, len(c)*2) 285 286 i := 0 287 for k, v := range c { 288 arr[i] = k 289 arr[i+1] = v 290 i += 2 291 } 292 293 return arr 294 }