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