github.com/gochain-io/gochain@v2.2.26+incompatible/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 // A Logger prints its log records by writing to a Handler. 15 // The Handler interface defines where and how log records are written. 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 // IsLogging returns true if global logging for level is enabled. 21 IsLogging(Lvl) bool 22 } 23 24 // FuncHandler returns a Handler that logs records with the given 25 // functions. 26 func FuncHandler(log func(r *Record) error, isLogging func(Lvl) bool) Handler { 27 return &funcHandler{ 28 log: log, 29 isLogging: isLogging, 30 } 31 } 32 33 type funcHandler struct { 34 log func(r *Record) error 35 isLogging func(Lvl) bool 36 } 37 38 func (h funcHandler) Log(r *Record) error { 39 return h.log(r) 40 } 41 42 func (h funcHandler) IsLogging(level Lvl) bool { 43 return h.isLogging(level) 44 } 45 46 // StreamHandler writes log records to an io.Writer 47 // with the given format. StreamHandler can be used 48 // to easily begin writing log records to other 49 // outputs. 50 // 51 // StreamHandler wraps itself with LazyHandler and SyncHandler 52 // to evaluate Lazy objects and perform safe concurrent writes. 53 func StreamHandler(wr io.Writer, fmtr Format) Handler { 54 h := FuncHandler(func(r *Record) error { 55 _, err := wr.Write(fmtr.Format(r)) 56 return err 57 }, func(Lvl) bool { return true }) 58 return LazyHandler(SyncHandler(h)) 59 } 60 61 // SyncHandler can be wrapped around a handler to guarantee that 62 // only a single Log operation can proceed at a time. It's necessary 63 // for thread-safe concurrent writes. 64 func SyncHandler(h Handler) Handler { 65 var mu sync.Mutex 66 return FuncHandler(func(r *Record) error { 67 mu.Lock() 68 err := h.Log(r) 69 mu.Unlock() 70 return err 71 }, h.IsLogging) 72 } 73 74 // FileHandler returns a handler which writes log records to the give file 75 // using the given format. If the path 76 // already exists, FileHandler will append to the given file. If it does not, 77 // FileHandler will create the file with mode 0644. 78 func FileHandler(path string, fmtr Format) (Handler, error) { 79 f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 80 if err != nil { 81 return nil, err 82 } 83 return closingHandler{f, StreamHandler(f, fmtr)}, nil 84 } 85 86 // NetHandler opens a socket to the given address and writes records 87 // over the connection. 88 func NetHandler(network, addr string, fmtr Format) (Handler, error) { 89 conn, err := net.Dial(network, addr) 90 if err != nil { 91 return nil, err 92 } 93 94 return closingHandler{conn, StreamHandler(conn, fmtr)}, nil 95 } 96 97 // XXX: closingHandler is essentially unused at the moment 98 // it's meant for a future time when the Handler interface supports 99 // a possible Close() operation 100 type closingHandler struct { 101 io.WriteCloser 102 Handler 103 } 104 105 func (h *closingHandler) Close() error { 106 return h.WriteCloser.Close() 107 } 108 109 // FilterHandler returns a Handler that only writes records to the 110 // wrapped Handler if the given function evaluates true. For example, 111 // to only log records where the 'err' key is not nil: 112 // 113 // logger.SetHandler(FilterHandler(func(r *Record) bool { 114 // for i := 0; i < len(r.Ctx); i += 2 { 115 // if r.Ctx[i] == "err" { 116 // return r.Ctx[i+1] != nil 117 // } 118 // } 119 // return false 120 // }, h)) 121 // 122 func FilterHandler(fn func(r *Record) bool, h Handler) Handler { 123 return FuncHandler(func(r *Record) error { 124 if fn(r) { 125 return h.Log(r) 126 } 127 return nil 128 }, h.IsLogging) 129 } 130 131 // LvlFilterHandler returns a Handler that only writes 132 // records which are less than the given verbosity 133 // level to the wrapped Handler. For example, to only 134 // log Error/Crit records: 135 // 136 // log.LvlFilterHandler(log.LvlError, log.StdoutHandler) 137 // 138 func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { 139 return FilterHandler(func(r *Record) (pass bool) { 140 return r.Lvl <= maxLvl 141 }, h) 142 } 143 144 // LazyHandler writes all values to the wrapped handler after evaluating 145 // any lazy functions in the record's context. It is already wrapped 146 // around StreamHandler and SyslogHandler in this library, you'll only need 147 // it if you write your own Handler. 148 func LazyHandler(h Handler) Handler { 149 return FuncHandler(func(r *Record) error { 150 // go through the values (odd indices) and reassign 151 // the values of any lazy fn to the result of its execution 152 hadErr := false 153 for i := 1; i < len(r.Ctx); i += 2 { 154 lz, ok := r.Ctx[i].(Lazy) 155 if ok { 156 v, err := evaluateLazy(lz) 157 if err != nil { 158 hadErr = true 159 r.Ctx[i] = err 160 } else { 161 if cs, ok := v.(stack.CallStack); ok { 162 v = cs.TrimBelow(r.Call).TrimRuntime() 163 } 164 r.Ctx[i] = v 165 } 166 } 167 } 168 169 if hadErr { 170 r.Ctx = append(r.Ctx, errorKey, "bad lazy") 171 } 172 173 return h.Log(r) 174 }, h.IsLogging) 175 } 176 177 func evaluateLazy(lz Lazy) (interface{}, error) { 178 t := reflect.TypeOf(lz.Fn) 179 180 if t.Kind() != reflect.Func { 181 return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) 182 } 183 184 if t.NumIn() > 0 { 185 return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) 186 } 187 188 if t.NumOut() == 0 { 189 return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) 190 } 191 192 value := reflect.ValueOf(lz.Fn) 193 results := value.Call([]reflect.Value{}) 194 if len(results) == 1 { 195 return results[0].Interface(), nil 196 } else { 197 values := make([]interface{}, len(results)) 198 for i, v := range results { 199 values[i] = v.Interface() 200 } 201 return values, nil 202 } 203 } 204 205 // DiscardHandler reports success for all writes but does nothing. 206 // It is useful for dynamically disabling logging at runtime via 207 // a Logger's SetHandler method. 208 func DiscardHandler() Handler { 209 return FuncHandler(func(r *Record) error { 210 return nil 211 }, func(Lvl) bool { return false }) 212 } 213 214 // The Must object provides the following Handler creation functions 215 // which instead of returning an error parameter only return a Handler 216 // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler 217 var Must muster 218 219 func must(h Handler, err error) Handler { 220 if err != nil { 221 panic(err) 222 } 223 return h 224 } 225 226 type muster struct{} 227 228 func (m muster) FileHandler(path string, fmtr Format) Handler { 229 return must(FileHandler(path, fmtr)) 230 } 231 232 func (m muster) NetHandler(network, addr string, fmtr Format) Handler { 233 return must(NetHandler(network, addr, fmtr)) 234 }