github.com/turingchain2020/turingchain@v1.1.21/common/log/log15/handler.go (about) 1 // Copyright Turing Corp. 2018 All Rights Reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package log15 6 7 import ( 8 "fmt" 9 "io" 10 "net" 11 "os" 12 "reflect" 13 "sync" 14 15 "github.com/go-stack/stack" 16 ) 17 18 // Handler interface defines where and how log records are written. 19 // A logger prints its log records by writing to a Handler. 20 // Handlers are composable, providing you great flexibility in combining 21 // them to achieve the logging structure that suits your applications. 22 type Handler interface { 23 Log(r *Record) error 24 MaxLevel() int 25 SetMaxLevel(int) 26 } 27 28 // FuncHandler returns a Handler that logs records with the given 29 // function. 30 func FuncHandler(maxLevel int, fn func(r *Record) error) Handler { 31 return &handlebase{fn, maxLevel} 32 } 33 34 type handlebase struct { 35 funcHandler func(r *Record) error 36 maxLevel int 37 } 38 39 func (h *handlebase) Log(r *Record) error { 40 return h.funcHandler(r) 41 } 42 43 func (h *handlebase) MaxLevel() int { 44 return h.maxLevel 45 } 46 47 func (h *handlebase) SetMaxLevel(maxLevel int) { 48 h.maxLevel = maxLevel 49 } 50 51 // StreamHandler writes log records to an io.Writer 52 // with the given format. StreamHandler can be used 53 // to easily begin writing log records to other 54 // outputs. 55 // 56 // StreamHandler wraps itself with LazyHandler and SyncHandler 57 // to evaluate Lazy objects and perform safe concurrent writes. 58 func StreamHandler(wr io.Writer, fmtr Format) Handler { 59 h := FuncHandler(int(LvlDebug), func(r *Record) error { 60 _, err := wr.Write(fmtr.Format(r)) 61 return err 62 }) 63 return LazyHandler(SyncHandler(h)) 64 } 65 66 // SyncHandler can be wrapped around a handler to guarantee that 67 // only a single Log operation can proceed at a time. It's necessary 68 // for thread-safe concurrent writes. 69 func SyncHandler(h Handler) Handler { 70 var mu sync.Mutex 71 return FuncHandler(h.MaxLevel(), func(r *Record) error { 72 mu.Lock() 73 err := h.Log(r) 74 mu.Unlock() 75 return err 76 }) 77 } 78 79 // FileHandler returns a handler which writes log records to the give file 80 // using the given format. If the path 81 // already exists, FileHandler will append to the given file. If it does not, 82 // FileHandler will create the file with mode 0644. 83 func FileHandler(path string, fmtr Format) (Handler, error) { 84 f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 85 if err != nil { 86 return nil, err 87 } 88 return closingHandler{f, StreamHandler(f, fmtr)}, nil 89 } 90 91 // NetHandler opens a socket to the given address and writes records 92 // over the connection. 93 func NetHandler(network, addr string, fmtr Format) (Handler, error) { 94 conn, err := net.Dial(network, addr) 95 if err != nil { 96 return nil, err 97 } 98 99 return closingHandler{conn, StreamHandler(conn, fmtr)}, nil 100 } 101 102 // XXX: closingHandler is essentially unused at the moment 103 // it's meant for a future time when the Handler interface supports 104 // a possible Close() operation 105 type closingHandler struct { 106 io.WriteCloser 107 Handler 108 } 109 110 func (h *closingHandler) Close() error { 111 return h.WriteCloser.Close() 112 } 113 114 // CallerFileHandler returns a Handler that adds the line number and file of 115 // the calling function to the context with key "caller". 116 func CallerFileHandler(h Handler) Handler { 117 return FuncHandler(h.MaxLevel(), func(r *Record) error { 118 r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) 119 return h.Log(r) 120 }) 121 } 122 123 // CallerFuncHandler returns a Handler that adds the calling function name to 124 // the context with key "fn". 125 func CallerFuncHandler(h Handler) Handler { 126 return FuncHandler(h.MaxLevel(), func(r *Record) error { 127 r.Ctx = append(r.Ctx, "fn", fmt.Sprintf("%+n", r.Call)) 128 return h.Log(r) 129 }) 130 } 131 132 // CallerStackHandler returns a Handler that adds a stack trace to the context 133 // with key "stack". The stack trace is formated as a space separated list of 134 // call sites inside matching []'s. The most recent call site is listed first. 135 // Each call site is formatted according to format. See the documentation of 136 // package github.com/go-stack/stack for the list of supported formats. 137 func CallerStackHandler(format string, h Handler) Handler { 138 return FuncHandler(h.MaxLevel(), func(r *Record) error { 139 s := stack.Trace().TrimBelow(r.Call).TrimRuntime() 140 if len(s) > 0 { 141 r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) 142 } 143 return h.Log(r) 144 }) 145 } 146 147 // FilterHandler returns a Handler that only writes records to the 148 // wrapped Handler if the given function evaluates true. For example, 149 // to only log records where the 'err' key is not nil: 150 // 151 // logger.SetHandler(FilterHandler(func(r *Record) bool { 152 // for i := 0; i < len(r.Ctx); i += 2 { 153 // if r.Ctx[i] == "err" { 154 // return r.Ctx[i+1] != nil 155 // } 156 // } 157 // return false 158 // }, h)) 159 // 160 func FilterHandler(fn func(r *Record) bool, h Handler) Handler { 161 return FuncHandler(h.MaxLevel(), func(r *Record) error { 162 if fn(r) { 163 return h.Log(r) 164 } 165 return nil 166 }) 167 } 168 169 // MatchFilterHandler returns a Handler that only writes records 170 // to the wrapped Handler if the given key in the logged 171 // context matches the value. For example, to only log records 172 // from your ui package: 173 // 174 // log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) 175 // 176 func MatchFilterHandler(key string, value interface{}, h Handler) Handler { 177 return FilterHandler(func(r *Record) (pass bool) { 178 switch key { 179 case r.KeyNames.Lvl: 180 return r.Lvl == value 181 case r.KeyNames.Time: 182 return r.Time == value 183 case r.KeyNames.Msg: 184 return r.Msg == value 185 } 186 187 for i := 0; i < len(r.Ctx); i += 2 { 188 if r.Ctx[i] == key { 189 return r.Ctx[i+1] == value 190 } 191 } 192 return false 193 }, h) 194 } 195 196 // LvlFilterHandler returns a Handler that only writes 197 // records which are less than the given verbosity 198 // level to the wrapped Handler. For example, to only 199 // log Error/Crit records: 200 // 201 // log.LvlFilterHandler(log.LvlError, log.StdoutHandler) 202 // 203 func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { 204 h.SetMaxLevel(int(maxLvl)) 205 return FilterHandler(func(r *Record) (pass bool) { 206 return r.Lvl <= maxLvl 207 }, h) 208 } 209 210 // MultiHandler dispatches any write to each of its handlers. 211 // This is useful for writing different types of log information 212 // to different locations. For example, to log to a file and 213 // standard error: 214 // 215 // log.MultiHandler( 216 // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), 217 // log.StderrHandler) 218 // 219 220 func maxLevelHanldes(hs []Handler) int { 221 maxLevel := 0 222 for _, handle := range hs { 223 if handle.MaxLevel() > maxLevel { 224 maxLevel = handle.MaxLevel() 225 } 226 } 227 return maxLevel 228 } 229 230 //MultiHandler ... 231 func MultiHandler(hs ...Handler) Handler { 232 return FuncHandler(maxLevelHanldes(hs), func(r *Record) error { 233 for _, h := range hs { 234 // what to do about failures? 235 h.Log(r) 236 } 237 return nil 238 }) 239 } 240 241 // FailoverHandler writes all log records to the first handler 242 // specified, but will failover and write to the second handler if 243 // the first handler has failed, and so on for all handlers specified. 244 // For example you might want to log to a network socket, but failover 245 // to writing to a file if the network fails, and then to 246 // standard out if the file write fails: 247 // 248 // log.FailoverHandler( 249 // log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), 250 // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), 251 // log.StdoutHandler) 252 // 253 // All writes that do not go to the first handler will add context with keys of 254 // the form "failover_err_{idx}" which explain the error encountered while 255 // trying to write to the handlers before them in the list. 256 func FailoverHandler(hs ...Handler) Handler { 257 return FuncHandler(maxLevelHanldes(hs), func(r *Record) error { 258 var err error 259 for i, h := range hs { 260 err = h.Log(r) 261 if err == nil { 262 return nil 263 } 264 r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) 265 } 266 return err 267 }) 268 } 269 270 // ChannelHandler writes all records to the given channel. 271 // It blocks if the channel is full. Useful for async processing 272 // of log messages, it's used by BufferedHandler. 273 func ChannelHandler(recs chan<- *Record, maxLevel int) Handler { 274 return FuncHandler(maxLevel, func(r *Record) error { 275 recs <- r 276 return nil 277 }) 278 } 279 280 // BufferedHandler writes all records to a buffered 281 // channel of the given size which flushes into the wrapped 282 // handler whenever it is available for writing. Since these 283 // writes happen asynchronously, all writes to a BufferedHandler 284 // never return an error and any errors from the wrapped handler are ignored. 285 func BufferedHandler(bufSize int, h Handler) Handler { 286 recs := make(chan *Record, bufSize) 287 go func() { 288 for m := range recs { 289 _ = h.Log(m) 290 } 291 }() 292 return ChannelHandler(recs, h.MaxLevel()) 293 } 294 295 // LazyHandler writes all values to the wrapped handler after evaluating 296 // any lazy functions in the record's context. It is already wrapped 297 // around StreamHandler and SyslogHandler in this library, you'll only need 298 // it if you write your own Handler. 299 func LazyHandler(h Handler) Handler { 300 return FuncHandler(h.MaxLevel(), func(r *Record) error { 301 // go through the values (odd indices) and reassign 302 // the values of any lazy fn to the result of its execution 303 hadErr := false 304 for i := 1; i < len(r.Ctx); i += 2 { 305 lz, ok := r.Ctx[i].(Lazy) 306 if ok { 307 v, err := evaluateLazy(lz) 308 if err != nil { 309 hadErr = true 310 r.Ctx[i] = err 311 } else { 312 if cs, ok := v.(stack.CallStack); ok { 313 v = cs.TrimBelow(r.Call).TrimRuntime() 314 } 315 r.Ctx[i] = v 316 } 317 } 318 } 319 320 if hadErr { 321 r.Ctx = append(r.Ctx, errorKey, "bad lazy") 322 } 323 324 return h.Log(r) 325 }) 326 } 327 328 func evaluateLazy(lz Lazy) (interface{}, error) { 329 t := reflect.TypeOf(lz.Fn) 330 331 if t.Kind() != reflect.Func { 332 return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) 333 } 334 335 if t.NumIn() > 0 { 336 return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) 337 } 338 339 if t.NumOut() == 0 { 340 return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) 341 } 342 343 value := reflect.ValueOf(lz.Fn) 344 results := value.Call([]reflect.Value{}) 345 if len(results) == 1 { 346 return results[0].Interface(), nil 347 } 348 values := make([]interface{}, len(results)) 349 for i, v := range results { 350 values[i] = v.Interface() 351 } 352 return values, nil 353 } 354 355 // DiscardHandler reports success for all writes but does nothing. 356 // It is useful for dynamically disabling logging at runtime via 357 // a Logger's SetHandler method. 358 func DiscardHandler() Handler { 359 return FuncHandler(int(LvlCrit), func(r *Record) error { 360 return nil 361 }) 362 } 363 364 // Must object provides the following Handler creation functions 365 // which instead of returning an error parameter only return a Handler 366 // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler 367 var Must muster 368 369 func must(h Handler, err error) Handler { 370 if err != nil { 371 panic(err) 372 } 373 return h 374 } 375 376 type muster struct{} 377 378 func (m muster) FileHandler(path string, fmtr Format) Handler { 379 return must(FileHandler(path, fmtr)) 380 } 381 382 func (m muster) NetHandler(network, addr string, fmtr Format) Handler { 383 return must(NetHandler(network, addr, fmtr)) 384 }