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