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