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