1package ext 2 3import ( 4 "os" 5 "sync" 6 "sync/atomic" 7 "unsafe" 8 9 log "github.com/inconshreveable/log15" 10) 11 12// EscalateErrHandler wraps another handler and passes all records through 13// unchanged except if the logged context contains a non-nil error 14// value in its context. In that case, the record's level is raised 15// to LvlError unless it was already more serious (LvlCrit). 16// 17// This allows you to log the result of all functions for debugging 18// and still capture error conditions when in production with a single 19// log line. As an example, the following the log record will be written 20// out only if there was an error writing a value to redis: 21// 22// logger := logext.EscalateErrHandler( 23// log.LvlFilterHandler(log.LvlInfo, log.StdoutHandler)) 24// 25// reply, err := redisConn.Do("SET", "foo", "bar") 26// logger.Debug("Wrote value to redis", "reply", reply, "err", err) 27// if err != nil { 28// return err 29// } 30// 31func EscalateErrHandler(h log.Handler) log.Handler { 32 return log.FuncHandler(func(r *log.Record) error { 33 if r.Lvl > log.LvlError { 34 for i := 1; i < len(r.Ctx); i++ { 35 if v, ok := r.Ctx[i].(error); ok && v != nil { 36 r.Lvl = log.LvlError 37 break 38 } 39 } 40 } 41 return h.Log(r) 42 }) 43} 44 45// SpeculativeHandler is a handler for speculative logging. It 46// keeps a ring buffer of the given size full of the last events 47// logged into it. When Flush is called, all buffered log records 48// are written to the wrapped handler. This is extremely for 49// continuosly capturing debug level output, but only flushing those 50// log records if an exceptional condition is encountered. 51func SpeculativeHandler(size int, h log.Handler) *Speculative { 52 return &Speculative{ 53 handler: h, 54 recs: make([]*log.Record, size), 55 } 56} 57 58// Speculative is the Log15.Handler. Read `SpeculativeHandler` for more information. 59type Speculative struct { 60 mu sync.Mutex 61 idx int 62 recs []*log.Record 63 handler log.Handler 64 full bool 65} 66 67// Log implements log15.Handler interface 68func (h *Speculative) Log(r *log.Record) error { 69 h.mu.Lock() 70 defer h.mu.Unlock() 71 h.recs[h.idx] = r 72 h.idx = (h.idx + 1) % len(h.recs) 73 h.full = h.full || h.idx == 0 74 return nil 75} 76 77// Flush logs all records on the handler. 78func (h *Speculative) Flush() { 79 recs := make([]*log.Record, 0) 80 func() { 81 h.mu.Lock() 82 defer h.mu.Unlock() 83 if h.full { 84 recs = append(recs, h.recs[h.idx:]...) 85 } 86 recs = append(recs, h.recs[:h.idx]...) 87 88 // reset state 89 h.full = false 90 h.idx = 0 91 }() 92 93 // don't hold the lock while we flush to the wrapped handler 94 for _, r := range recs { 95 h.handler.Log(r) 96 } 97} 98 99// HotSwapHandler wraps another handler that may swapped out 100// dynamically at runtime in a thread-safe fashion. 101// HotSwapHandler is the same functionality 102// used to implement the SetHandler method for the default 103// implementation of Logger. 104func HotSwapHandler(h log.Handler) *HotSwap { 105 hs := new(HotSwap) 106 hs.Swap(h) 107 return hs 108} 109 110// HotSwap is the Log15.Handler. Read `HotSwapHandler` for more information. 111type HotSwap struct { 112 handler unsafe.Pointer 113} 114 115// Log implements log15.Handler interface. 116func (h *HotSwap) Log(r *log.Record) error { 117 return (*(*log.Handler)(atomic.LoadPointer(&h.handler))).Log(r) 118} 119 120// Swap atomically the logger handler. 121func (h *HotSwap) Swap(newHandler log.Handler) { 122 atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) 123} 124 125// FatalHandler makes critical errors exit the program 126// immediately, much like the log.Fatal* methods from the 127// standard log package 128func FatalHandler(h log.Handler) log.Handler { 129 return log.FuncHandler(func(r *log.Record) error { 130 err := h.Log(r) 131 if r.Lvl == log.LvlCrit { 132 os.Exit(1) 133 } 134 return err 135 }) 136} 137