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