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