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