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