github.com/luckypickle/go-ethereum-vet@v1.14.2/log/handler.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "io" 6 "net" 7 "os" 8 "reflect" 9 "sync" 10 11 "io/ioutil" 12 "path/filepath" 13 "regexp" 14 "strings" 15 16 "github.com/go-stack/stack" 17 ) 18 19 // Handler defines where and how log records are written. 20 // A Logger prints its log records by writing to a Handler. 21 // Handlers are composable, providing you great flexibility in combining 22 // them to achieve the logging structure that suits your applications. 23 type Handler interface { 24 Log(r *Record) error 25 } 26 27 // FuncHandler returns a Handler that logs records with the given 28 // function. 29 func FuncHandler(fn func(r *Record) error) Handler { 30 return funcHandler(fn) 31 } 32 33 type funcHandler func(r *Record) error 34 35 func (h funcHandler) Log(r *Record) error { 36 return h(r) 37 } 38 39 // StreamHandler writes log records to an io.Writer 40 // with the given format. StreamHandler can be used 41 // to easily begin writing log records to other 42 // outputs. 43 // 44 // StreamHandler wraps itself with LazyHandler and SyncHandler 45 // to evaluate Lazy objects and perform safe concurrent writes. 46 func StreamHandler(wr io.Writer, fmtr Format) Handler { 47 h := FuncHandler(func(r *Record) error { 48 _, err := wr.Write(fmtr.Format(r)) 49 return err 50 }) 51 return LazyHandler(SyncHandler(h)) 52 } 53 54 // SyncHandler can be wrapped around a handler to guarantee that 55 // only a single Log operation can proceed at a time. It's necessary 56 // for thread-safe concurrent writes. 57 func SyncHandler(h Handler) Handler { 58 var mu sync.Mutex 59 return FuncHandler(func(r *Record) error { 60 defer mu.Unlock() 61 mu.Lock() 62 return h.Log(r) 63 }) 64 } 65 66 // FileHandler returns a handler which writes log records to the give file 67 // using the given format. If the path 68 // already exists, FileHandler will append to the given file. If it does not, 69 // FileHandler will create the file with mode 0644. 70 func FileHandler(path string, fmtr Format) (Handler, error) { 71 f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 72 if err != nil { 73 return nil, err 74 } 75 return closingHandler{f, StreamHandler(f, fmtr)}, nil 76 } 77 78 // countingWriter wraps a WriteCloser object in order to count the written bytes. 79 type countingWriter struct { 80 w io.WriteCloser // the wrapped object 81 count uint // number of bytes written 82 } 83 84 // Write increments the byte counter by the number of bytes written. 85 // Implements the WriteCloser interface. 86 func (w *countingWriter) Write(p []byte) (n int, err error) { 87 n, err = w.w.Write(p) 88 w.count += uint(n) 89 return n, err 90 } 91 92 // Close implements the WriteCloser interface. 93 func (w *countingWriter) Close() error { 94 return w.w.Close() 95 } 96 97 // prepFile opens the log file at the given path, and cuts off the invalid part 98 // from the end, because the previous execution could have been finished by interruption. 99 // Assumes that every line ended by '\n' contains a valid log record. 100 func prepFile(path string) (*countingWriter, error) { 101 f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0600) 102 if err != nil { 103 return nil, err 104 } 105 _, err = f.Seek(-1, io.SeekEnd) 106 if err != nil { 107 return nil, err 108 } 109 buf := make([]byte, 1) 110 var cut int64 111 for { 112 if _, err := f.Read(buf); err != nil { 113 return nil, err 114 } 115 if buf[0] == '\n' { 116 break 117 } 118 if _, err = f.Seek(-2, io.SeekCurrent); err != nil { 119 return nil, err 120 } 121 cut++ 122 } 123 fi, err := f.Stat() 124 if err != nil { 125 return nil, err 126 } 127 ns := fi.Size() - cut 128 if err = f.Truncate(ns); err != nil { 129 return nil, err 130 } 131 return &countingWriter{w: f, count: uint(ns)}, nil 132 } 133 134 // RotatingFileHandler returns a handler which writes log records to file chunks 135 // at the given path. When a file's size reaches the limit, the handler creates 136 // a new file named after the timestamp of the first log record it will contain. 137 func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) { 138 if err := os.MkdirAll(path, 0700); err != nil { 139 return nil, err 140 } 141 files, err := ioutil.ReadDir(path) 142 if err != nil { 143 return nil, err 144 } 145 re := regexp.MustCompile(`\.log$`) 146 last := len(files) - 1 147 for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) { 148 last-- 149 } 150 var counter *countingWriter 151 if last >= 0 && files[last].Size() < int64(limit) { 152 // Open the last file, and continue to write into it until it's size reaches the limit. 153 if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil { 154 return nil, err 155 } 156 } 157 if counter == nil { 158 counter = new(countingWriter) 159 } 160 h := StreamHandler(counter, formatter) 161 162 return FuncHandler(func(r *Record) error { 163 if counter.count > limit { 164 counter.Close() 165 counter.w = nil 166 } 167 if counter.w == nil { 168 f, err := os.OpenFile( 169 filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("060102150405.00"), ".", "", 1))), 170 os.O_CREATE|os.O_APPEND|os.O_WRONLY, 171 0600, 172 ) 173 if err != nil { 174 return err 175 } 176 counter.w = f 177 counter.count = 0 178 } 179 return h.Log(r) 180 }), nil 181 } 182 183 // NetHandler opens a socket to the given address and writes records 184 // over the connection. 185 func NetHandler(network, addr string, fmtr Format) (Handler, error) { 186 conn, err := net.Dial(network, addr) 187 if err != nil { 188 return nil, err 189 } 190 191 return closingHandler{conn, StreamHandler(conn, fmtr)}, nil 192 } 193 194 // XXX: closingHandler is essentially unused at the moment 195 // it's meant for a future time when the Handler interface supports 196 // a possible Close() operation 197 type closingHandler struct { 198 io.WriteCloser 199 Handler 200 } 201 202 func (h *closingHandler) Close() error { 203 return h.WriteCloser.Close() 204 } 205 206 // CallerFileHandler returns a Handler that adds the line number and file of 207 // the calling function to the context with key "caller". 208 func CallerFileHandler(h Handler) Handler { 209 return FuncHandler(func(r *Record) error { 210 r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) 211 return h.Log(r) 212 }) 213 } 214 215 // CallerFuncHandler returns a Handler that adds the calling function name to 216 // the context with key "fn". 217 func CallerFuncHandler(h Handler) Handler { 218 return FuncHandler(func(r *Record) error { 219 r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call)) 220 return h.Log(r) 221 }) 222 } 223 224 // This function is here to please go vet on Go < 1.8. 225 func formatCall(format string, c stack.Call) string { 226 return fmt.Sprintf(format, c) 227 } 228 229 // CallerStackHandler returns a Handler that adds a stack trace to the context 230 // with key "stack". The stack trace is formated as a space separated list of 231 // call sites inside matching []'s. The most recent call site is listed first. 232 // Each call site is formatted according to format. See the documentation of 233 // package github.com/go-stack/stack for the list of supported formats. 234 func CallerStackHandler(format string, h Handler) Handler { 235 return FuncHandler(func(r *Record) error { 236 s := stack.Trace().TrimBelow(r.Call).TrimRuntime() 237 if len(s) > 0 { 238 r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) 239 } 240 return h.Log(r) 241 }) 242 } 243 244 // FilterHandler returns a Handler that only writes records to the 245 // wrapped Handler if the given function evaluates true. For example, 246 // to only log records where the 'err' key is not nil: 247 // 248 // logger.SetHandler(FilterHandler(func(r *Record) bool { 249 // for i := 0; i < len(r.Ctx); i += 2 { 250 // if r.Ctx[i] == "err" { 251 // return r.Ctx[i+1] != nil 252 // } 253 // } 254 // return false 255 // }, h)) 256 // 257 func FilterHandler(fn func(r *Record) bool, h Handler) Handler { 258 return FuncHandler(func(r *Record) error { 259 if fn(r) { 260 return h.Log(r) 261 } 262 return nil 263 }) 264 } 265 266 // MatchFilterHandler returns a Handler that only writes records 267 // to the wrapped Handler if the given key in the logged 268 // context matches the value. For example, to only log records 269 // from your ui package: 270 // 271 // log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) 272 // 273 func MatchFilterHandler(key string, value interface{}, h Handler) Handler { 274 return FilterHandler(func(r *Record) (pass bool) { 275 switch key { 276 case r.KeyNames.Lvl: 277 return r.Lvl == value 278 case r.KeyNames.Time: 279 return r.Time == value 280 case r.KeyNames.Msg: 281 return r.Msg == value 282 } 283 284 for i := 0; i < len(r.Ctx); i += 2 { 285 if r.Ctx[i] == key { 286 return r.Ctx[i+1] == value 287 } 288 } 289 return false 290 }, h) 291 } 292 293 // LvlFilterHandler returns a Handler that only writes 294 // records which are less than the given verbosity 295 // level to the wrapped Handler. For example, to only 296 // log Error/Crit records: 297 // 298 // log.LvlFilterHandler(log.LvlError, log.StdoutHandler) 299 // 300 func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { 301 return FilterHandler(func(r *Record) (pass bool) { 302 return r.Lvl <= maxLvl 303 }, h) 304 } 305 306 // MultiHandler dispatches any write to each of its handlers. 307 // This is useful for writing different types of log information 308 // to different locations. For example, to log to a file and 309 // standard error: 310 // 311 // log.MultiHandler( 312 // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), 313 // log.StderrHandler) 314 // 315 func MultiHandler(hs ...Handler) Handler { 316 return FuncHandler(func(r *Record) error { 317 for _, h := range hs { 318 // what to do about failures? 319 h.Log(r) 320 } 321 return nil 322 }) 323 } 324 325 // FailoverHandler writes all log records to the first handler 326 // specified, but will failover and write to the second handler if 327 // the first handler has failed, and so on for all handlers specified. 328 // For example you might want to log to a network socket, but failover 329 // to writing to a file if the network fails, and then to 330 // standard out if the file write fails: 331 // 332 // log.FailoverHandler( 333 // log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), 334 // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), 335 // log.StdoutHandler) 336 // 337 // All writes that do not go to the first handler will add context with keys of 338 // the form "failover_err_{idx}" which explain the error encountered while 339 // trying to write to the handlers before them in the list. 340 func FailoverHandler(hs ...Handler) Handler { 341 return FuncHandler(func(r *Record) error { 342 var err error 343 for i, h := range hs { 344 err = h.Log(r) 345 if err == nil { 346 return nil 347 } 348 r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) 349 } 350 351 return err 352 }) 353 } 354 355 // ChannelHandler writes all records to the given channel. 356 // It blocks if the channel is full. Useful for async processing 357 // of log messages, it's used by BufferedHandler. 358 func ChannelHandler(recs chan<- *Record) Handler { 359 return FuncHandler(func(r *Record) error { 360 recs <- r 361 return nil 362 }) 363 } 364 365 // BufferedHandler writes all records to a buffered 366 // channel of the given size which flushes into the wrapped 367 // handler whenever it is available for writing. Since these 368 // writes happen asynchronously, all writes to a BufferedHandler 369 // never return an error and any errors from the wrapped handler are ignored. 370 func BufferedHandler(bufSize int, h Handler) Handler { 371 recs := make(chan *Record, bufSize) 372 go func() { 373 for m := range recs { 374 _ = h.Log(m) 375 } 376 }() 377 return ChannelHandler(recs) 378 } 379 380 // LazyHandler writes all values to the wrapped handler after evaluating 381 // any lazy functions in the record's context. It is already wrapped 382 // around StreamHandler and SyslogHandler in this library, you'll only need 383 // it if you write your own Handler. 384 func LazyHandler(h Handler) Handler { 385 return FuncHandler(func(r *Record) error { 386 // go through the values (odd indices) and reassign 387 // the values of any lazy fn to the result of its execution 388 hadErr := false 389 for i := 1; i < len(r.Ctx); i += 2 { 390 lz, ok := r.Ctx[i].(Lazy) 391 if ok { 392 v, err := evaluateLazy(lz) 393 if err != nil { 394 hadErr = true 395 r.Ctx[i] = err 396 } else { 397 if cs, ok := v.(stack.CallStack); ok { 398 v = cs.TrimBelow(r.Call).TrimRuntime() 399 } 400 r.Ctx[i] = v 401 } 402 } 403 } 404 405 if hadErr { 406 r.Ctx = append(r.Ctx, errorKey, "bad lazy") 407 } 408 409 return h.Log(r) 410 }) 411 } 412 413 func evaluateLazy(lz Lazy) (interface{}, error) { 414 t := reflect.TypeOf(lz.Fn) 415 416 if t.Kind() != reflect.Func { 417 return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) 418 } 419 420 if t.NumIn() > 0 { 421 return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) 422 } 423 424 if t.NumOut() == 0 { 425 return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) 426 } 427 428 value := reflect.ValueOf(lz.Fn) 429 results := value.Call([]reflect.Value{}) 430 if len(results) == 1 { 431 return results[0].Interface(), nil 432 } 433 values := make([]interface{}, len(results)) 434 for i, v := range results { 435 values[i] = v.Interface() 436 } 437 return values, nil 438 } 439 440 // DiscardHandler reports success for all writes but does nothing. 441 // It is useful for dynamically disabling logging at runtime via 442 // a Logger's SetHandler method. 443 func DiscardHandler() Handler { 444 return FuncHandler(func(r *Record) error { 445 return nil 446 }) 447 } 448 449 // Must provides the following Handler creation functions 450 // which instead of returning an error parameter only return a Handler 451 // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler 452 var Must muster 453 454 func must(h Handler, err error) Handler { 455 if err != nil { 456 panic(err) 457 } 458 return h 459 } 460 461 type muster struct{} 462 463 func (m muster) FileHandler(path string, fmtr Format) Handler { 464 return must(FileHandler(path, fmtr)) 465 } 466 467 func (m muster) NetHandler(network, addr string, fmtr Format) Handler { 468 return must(NetHandler(network, addr, fmtr)) 469 }