github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/log/handler.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:41</date> 10 //</624342646428536832> 11 12 package log 13 14 import ( 15 "fmt" 16 "io" 17 "net" 18 "os" 19 "reflect" 20 "sync" 21 22 "io/ioutil" 23 "path/filepath" 24 "regexp" 25 "strings" 26 27 "github.com/go-stack/stack" 28 ) 29 30 //处理程序定义日志记录的写入位置和方式。 31 //记录器通过写入处理程序来打印其日志记录。 32 //处理程序是可组合的,为您提供了很大的组合灵活性 33 //它们可以实现适合您的应用程序的日志结构。 34 type Handler interface { 35 Log(r *Record) error 36 } 37 38 //funchandler返回一个处理程序,该处理程序用给定的 39 //功能。 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将日志记录写入IO.Writer 51 //以给定的格式。可以使用流处理程序 52 //轻松开始向其他人写入日志记录 53 //输出。 54 // 55 //StreamHandler使用LazyHandler和Synchandler进行自我包装 56 //评估惰性对象并执行安全的并发写入。 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 //可以将同步处理程序包装在处理程序周围,以确保 66 //一次只能执行一个日志操作。这是必要的 67 //用于线程安全的并发写入。 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 //file handler返回一个将日志记录写入给定文件的处理程序 78 //使用给定格式。如果路径 79 //文件处理程序将附加到给定的文件。如果没有, 80 //文件处理程序将使用模式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 //CountingWriter包装WriteCloser对象以计算写入的字节数。 90 type countingWriter struct { 91 w io.WriteCloser //被包裹的物体 92 count uint //写入的字节数 93 } 94 95 //写入将字节计数器增加写入的字节数。 96 //实现WriteCloser接口。 97 func (w *countingWriter) Write(p []byte) (n int, err error) { 98 n, err = w.w.Write(p) 99 w.count += uint(n) 100 return n, err 101 } 102 103 //close实现了writecloser接口。 104 func (w *countingWriter) Close() error { 105 return w.w.Close() 106 } 107 108 //prepfile在给定路径打开日志文件,并切断无效部分 109 //因为上一次执行可能被中断而结束。 110 //假定以'\n'结尾的每一行都包含有效的日志记录。 111 func prepFile(path string) (*countingWriter, error) { 112 f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0600) 113 if err != nil { 114 return nil, err 115 } 116 _, err = f.Seek(-1, io.SeekEnd) 117 if err != nil { 118 return nil, err 119 } 120 buf := make([]byte, 1) 121 var cut int64 122 for { 123 if _, err := f.Read(buf); err != nil { 124 return nil, err 125 } 126 if buf[0] == '\n' { 127 break 128 } 129 if _, err = f.Seek(-2, io.SeekCurrent); err != nil { 130 return nil, err 131 } 132 cut++ 133 } 134 fi, err := f.Stat() 135 if err != nil { 136 return nil, err 137 } 138 ns := fi.Size() - cut 139 if err = f.Truncate(ns); err != nil { 140 return nil, err 141 } 142 return &countingWriter{w: f, count: uint(ns)}, nil 143 } 144 145 //RotatingFileHandler返回一个将日志记录写入文件块的处理程序 146 //在给定的路径上。当文件大小达到限制时,处理程序将创建 147 //以将包含的第一条日志记录的时间戳命名的新文件。 148 func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) { 149 if err := os.MkdirAll(path, 0700); err != nil { 150 return nil, err 151 } 152 files, err := ioutil.ReadDir(path) 153 if err != nil { 154 return nil, err 155 } 156 re := regexp.MustCompile(`\.log$`) 157 last := len(files) - 1 158 for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) { 159 last-- 160 } 161 var counter *countingWriter 162 if last >= 0 && files[last].Size() < int64(limit) { 163 //打开最后一个文件,继续写入,直到其大小达到限制。 164 if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil { 165 return nil, err 166 } 167 } 168 if counter == nil { 169 counter = new(countingWriter) 170 } 171 h := StreamHandler(counter, formatter) 172 173 return FuncHandler(func(r *Record) error { 174 if counter.count > limit { 175 counter.Close() 176 counter.w = nil 177 } 178 if counter.w == nil { 179 f, err := os.OpenFile( 180 filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("060102150405.00"), ".", "", 1))), 181 os.O_CREATE|os.O_APPEND|os.O_WRONLY, 182 0600, 183 ) 184 if err != nil { 185 return err 186 } 187 counter.w = f 188 counter.count = 0 189 } 190 return h.Log(r) 191 }), nil 192 } 193 194 //nethandler打开指定地址的套接字并写入记录 195 //通过连接。 196 func NetHandler(network, addr string, fmtr Format) (Handler, error) { 197 conn, err := net.Dial(network, addr) 198 if err != nil { 199 return nil, err 200 } 201 202 return closingHandler{conn, StreamHandler(conn, fmtr)}, nil 203 } 204 205 //xxx:closinghandler目前基本上没有使用过 206 //当处理程序接口支持 207 //可能的close()操作 208 type closingHandler struct { 209 io.WriteCloser 210 Handler 211 } 212 213 func (h *closingHandler) Close() error { 214 return h.WriteCloser.Close() 215 } 216 217 //CallerFileHandler返回一个处理程序,该处理程序添加 218 //使用键“caller”调用上下文的函数。 219 func CallerFileHandler(h Handler) Handler { 220 return FuncHandler(func(r *Record) error { 221 r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) 222 return h.Log(r) 223 }) 224 } 225 226 //CallerFundler返回一个将调用函数名添加到 227 //带有键“fn”的上下文。 228 func CallerFuncHandler(h Handler) Handler { 229 return FuncHandler(func(r *Record) error { 230 r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call)) 231 return h.Log(r) 232 }) 233 } 234 235 //此函数用于请在go<1.8时进行vet。 236 func formatCall(format string, c stack.Call) string { 237 return fmt.Sprintf(format, c) 238 } 239 240 //CallersTackHandler返回一个向上下文添加堆栈跟踪的处理程序 241 //用“叠加”键。堆栈跟踪的格式为以空格分隔的 242 //匹配的[]中的呼叫站点。首先列出最近的呼叫站点。 243 //每个呼叫站点都按照格式进行格式化。参见以下文件: 244 //包github.com/go-stack/stack以获取支持的格式列表。 245 func CallerStackHandler(format string, h Handler) Handler { 246 return FuncHandler(func(r *Record) error { 247 s := stack.Trace().TrimBelow(r.Call).TrimRuntime() 248 if len(s) > 0 { 249 r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) 250 } 251 return h.Log(r) 252 }) 253 } 254 255 //filterhandler返回只将记录写入 256 //如果给定函数的计算结果为true,则包装处理程序。例如, 257 //仅记录“err”键不为零的记录: 258 // 259 //logger.sethandler(filterhandler(func(r*record)bool_ 260 //对于i:=0;i<len(r.ctx);i+=2 261 //如果r.ctx[i]=“err” 262 //返回r.ctx[i+1]!=零 263 //} 264 //} 265 //返回假 266 //},h) 267 // 268 func FilterHandler(fn func(r *Record) bool, h Handler) Handler { 269 return FuncHandler(func(r *Record) error { 270 if fn(r) { 271 return h.Log(r) 272 } 273 return nil 274 }) 275 } 276 277 //MatchFilterHandler返回只写入记录的处理程序 278 //如果日志中的给定键 279 //上下文与值匹配。例如,只记录 280 //从您的UI包: 281 // 282 //log.matchfilterhandler(“pkg”,“app/ui”,log.stdouthandler) 283 // 284 func MatchFilterHandler(key string, value interface{}, h Handler) Handler { 285 return FilterHandler(func(r *Record) (pass bool) { 286 switch key { 287 case r.KeyNames.Lvl: 288 return r.Lvl == value 289 case r.KeyNames.Time: 290 return r.Time == value 291 case r.KeyNames.Msg: 292 return r.Msg == value 293 } 294 295 for i := 0; i < len(r.Ctx); i += 2 { 296 if r.Ctx[i] == key { 297 return r.Ctx[i+1] == value 298 } 299 } 300 return false 301 }, h) 302 } 303 304 //lvlFilterHandler返回一个只写 305 //低于给定详细程度的记录 306 //级别为包装处理程序。例如,仅 307 //日志错误/crit记录: 308 // 309 //log.lvlfilterhandler(log.lvlerror,log.stdouthandler) 310 // 311 func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { 312 return FilterHandler(func(r *Record) (pass bool) { 313 return r.Lvl <= maxLvl 314 }, h) 315 } 316 317 //多处理程序向其每个处理程序发送任何写操作。 318 //这对于写入不同类型的日志信息很有用 319 //去不同的地方。例如,登录到一个文件并 320 //标准误差: 321 // 322 //日志.multihandler( 323 //log.must.filehandler(“/var/log/app.log”,log.logfmtformat()), 324 //日志.stderrhandler) 325 // 326 func MultiHandler(hs ...Handler) Handler { 327 return FuncHandler(func(r *Record) error { 328 for _, h := range hs { 329 //如何处理失败? 330 h.Log(r) 331 } 332 return nil 333 }) 334 } 335 336 //failhandler将所有日志记录写入第一个处理程序 337 //已指定,但如果 338 //第一个处理程序失败,对所有指定的处理程序依此类推。 339 //例如,您可能希望登录到网络套接字,但故障转移 340 //如果网络出现故障,则写入文件,然后 341 //如果文件写入失败,则标准输出: 342 // 343 //日志.failhandler( 344 //log.must.nethandler(“tcp”,“:9090”,log.jsonformat()), 345 //log.must.filehandler(“/var/log/app.log”,log.logfmtformat()), 346 //日志.stdouthandler) 347 // 348 //不转到第一个处理程序的所有写入操作都将添加键为的上下文 349 //“故障转移”窗体,用于解释在 350 //尝试写入列表中处理程序之前的处理程序。 351 func FailoverHandler(hs ...Handler) Handler { 352 return FuncHandler(func(r *Record) error { 353 var err error 354 for i, h := range hs { 355 err = h.Log(r) 356 if err == nil { 357 return nil 358 } 359 r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) 360 } 361 362 return err 363 }) 364 } 365 366 //channelhandler将所有记录写入给定的通道。 367 //如果通道已满,则会阻塞。用于异步处理 368 //对于日志消息,它由BufferedHandler使用。 369 func ChannelHandler(recs chan<- *Record) Handler { 370 return FuncHandler(func(r *Record) error { 371 recs <- r 372 return nil 373 }) 374 } 375 376 //BufferedHandler将所有记录写入缓冲的 377 //给定大小的通道,冲入包装的 378 //处理程序,只要它可用于写入。既然这些 379 //写操作是异步进行的,所有写操作都是对BufferedHandler进行的 380 //永远不要返回错误,包装处理程序中的任何错误都将被忽略。 381 func BufferedHandler(bufSize int, h Handler) Handler { 382 recs := make(chan *Record, bufSize) 383 go func() { 384 for m := range recs { 385 _ = h.Log(m) 386 } 387 }() 388 return ChannelHandler(recs) 389 } 390 391 //Lazyhandler在评估后将所有值写入打包的处理程序 392 //记录上下文中的任何惰性函数。它已经包好了 393 //关于这个库中的streamhandler和sysloghandler,您只需要 394 //如果您编写自己的处理程序。 395 func LazyHandler(h Handler) Handler { 396 return FuncHandler(func(r *Record) error { 397 //遍历值(奇数索引)并重新分配 398 //任何延迟fn对其执行结果的值 399 hadErr := false 400 for i := 1; i < len(r.Ctx); i += 2 { 401 lz, ok := r.Ctx[i].(Lazy) 402 if ok { 403 v, err := evaluateLazy(lz) 404 if err != nil { 405 hadErr = true 406 r.Ctx[i] = err 407 } else { 408 if cs, ok := v.(stack.CallStack); ok { 409 v = cs.TrimBelow(r.Call).TrimRuntime() 410 } 411 r.Ctx[i] = v 412 } 413 } 414 } 415 416 if hadErr { 417 r.Ctx = append(r.Ctx, errorKey, "bad lazy") 418 } 419 420 return h.Log(r) 421 }) 422 } 423 424 func evaluateLazy(lz Lazy) (interface{}, error) { 425 t := reflect.TypeOf(lz.Fn) 426 427 if t.Kind() != reflect.Func { 428 return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) 429 } 430 431 if t.NumIn() > 0 { 432 return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) 433 } 434 435 if t.NumOut() == 0 { 436 return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) 437 } 438 439 value := reflect.ValueOf(lz.Fn) 440 results := value.Call([]reflect.Value{}) 441 if len(results) == 1 { 442 return results[0].Interface(), nil 443 } 444 values := make([]interface{}, len(results)) 445 for i, v := range results { 446 values[i] = v.Interface() 447 } 448 return values, nil 449 } 450 451 //DiscardHandler报告所有写入操作均成功,但不执行任何操作。 452 //它对于在运行时通过 453 //记录器的sethandler方法。 454 func DiscardHandler() Handler { 455 return FuncHandler(func(r *Record) error { 456 return nil 457 }) 458 } 459 460 //必须提供以下处理程序创建函数 461 //它不返回错误参数只返回一个处理程序 462 //失败时出现恐慌:filehandler、nethandler、sysloghandler、syslognethandler 463 var Must muster 464 465 func must(h Handler, err error) Handler { 466 if err != nil { 467 panic(err) 468 } 469 return h 470 } 471 472 type muster struct{} 473 474 func (m muster) FileHandler(path string, fmtr Format) Handler { 475 return must(FileHandler(path, fmtr)) 476 } 477 478 func (m muster) NetHandler(network, addr string, fmtr Format) Handler { 479 return must(NetHandler(network, addr, fmtr)) 480 } 481