github.com/luckypickle/go-ethereum-vet@v1.14.2/log/handler.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"reflect"
     9  	"sync"
    10  
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"regexp"
    14  	"strings"
    15  
    16  	"github.com/go-stack/stack"
    17  )
    18  
    19  // Handler defines where and how log records are written.
    20  // A Logger prints its log records by writing to a Handler.
    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, 0644)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	return closingHandler{f, StreamHandler(f, fmtr)}, nil
    76  }
    77  
    78  // countingWriter wraps a WriteCloser object in order to count the written bytes.
    79  type countingWriter struct {
    80  	w     io.WriteCloser // the wrapped object
    81  	count uint           // number of bytes written
    82  }
    83  
    84  // Write increments the byte counter by the number of bytes written.
    85  // Implements the WriteCloser interface.
    86  func (w *countingWriter) Write(p []byte) (n int, err error) {
    87  	n, err = w.w.Write(p)
    88  	w.count += uint(n)
    89  	return n, err
    90  }
    91  
    92  // Close implements the WriteCloser interface.
    93  func (w *countingWriter) Close() error {
    94  	return w.w.Close()
    95  }
    96  
    97  // prepFile opens the log file at the given path, and cuts off the invalid part
    98  // from the end, because the previous execution could have been finished by interruption.
    99  // Assumes that every line ended by '\n' contains a valid log record.
   100  func prepFile(path string) (*countingWriter, error) {
   101  	f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0600)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	_, err = f.Seek(-1, io.SeekEnd)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	buf := make([]byte, 1)
   110  	var cut int64
   111  	for {
   112  		if _, err := f.Read(buf); err != nil {
   113  			return nil, err
   114  		}
   115  		if buf[0] == '\n' {
   116  			break
   117  		}
   118  		if _, err = f.Seek(-2, io.SeekCurrent); err != nil {
   119  			return nil, err
   120  		}
   121  		cut++
   122  	}
   123  	fi, err := f.Stat()
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	ns := fi.Size() - cut
   128  	if err = f.Truncate(ns); err != nil {
   129  		return nil, err
   130  	}
   131  	return &countingWriter{w: f, count: uint(ns)}, nil
   132  }
   133  
   134  // RotatingFileHandler returns a handler which writes log records to file chunks
   135  // at the given path. When a file's size reaches the limit, the handler creates
   136  // a new file named after the timestamp of the first log record it will contain.
   137  func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) {
   138  	if err := os.MkdirAll(path, 0700); err != nil {
   139  		return nil, err
   140  	}
   141  	files, err := ioutil.ReadDir(path)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	re := regexp.MustCompile(`\.log$`)
   146  	last := len(files) - 1
   147  	for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) {
   148  		last--
   149  	}
   150  	var counter *countingWriter
   151  	if last >= 0 && files[last].Size() < int64(limit) {
   152  		// Open the last file, and continue to write into it until it's size reaches the limit.
   153  		if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil {
   154  			return nil, err
   155  		}
   156  	}
   157  	if counter == nil {
   158  		counter = new(countingWriter)
   159  	}
   160  	h := StreamHandler(counter, formatter)
   161  
   162  	return FuncHandler(func(r *Record) error {
   163  		if counter.count > limit {
   164  			counter.Close()
   165  			counter.w = nil
   166  		}
   167  		if counter.w == nil {
   168  			f, err := os.OpenFile(
   169  				filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("060102150405.00"), ".", "", 1))),
   170  				os.O_CREATE|os.O_APPEND|os.O_WRONLY,
   171  				0600,
   172  			)
   173  			if err != nil {
   174  				return err
   175  			}
   176  			counter.w = f
   177  			counter.count = 0
   178  		}
   179  		return h.Log(r)
   180  	}), nil
   181  }
   182  
   183  // NetHandler opens a socket to the given address and writes records
   184  // over the connection.
   185  func NetHandler(network, addr string, fmtr Format) (Handler, error) {
   186  	conn, err := net.Dial(network, addr)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
   192  }
   193  
   194  // XXX: closingHandler is essentially unused at the moment
   195  // it's meant for a future time when the Handler interface supports
   196  // a possible Close() operation
   197  type closingHandler struct {
   198  	io.WriteCloser
   199  	Handler
   200  }
   201  
   202  func (h *closingHandler) Close() error {
   203  	return h.WriteCloser.Close()
   204  }
   205  
   206  // CallerFileHandler returns a Handler that adds the line number and file of
   207  // the calling function to the context with key "caller".
   208  func CallerFileHandler(h Handler) Handler {
   209  	return FuncHandler(func(r *Record) error {
   210  		r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call))
   211  		return h.Log(r)
   212  	})
   213  }
   214  
   215  // CallerFuncHandler returns a Handler that adds the calling function name to
   216  // the context with key "fn".
   217  func CallerFuncHandler(h Handler) Handler {
   218  	return FuncHandler(func(r *Record) error {
   219  		r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
   220  		return h.Log(r)
   221  	})
   222  }
   223  
   224  // This function is here to please go vet on Go < 1.8.
   225  func formatCall(format string, c stack.Call) string {
   226  	return fmt.Sprintf(format, c)
   227  }
   228  
   229  // CallerStackHandler returns a Handler that adds a stack trace to the context
   230  // with key "stack". The stack trace is formated as a space separated list of
   231  // call sites inside matching []'s. The most recent call site is listed first.
   232  // Each call site is formatted according to format. See the documentation of
   233  // package github.com/go-stack/stack for the list of supported formats.
   234  func CallerStackHandler(format string, h Handler) Handler {
   235  	return FuncHandler(func(r *Record) error {
   236  		s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
   237  		if len(s) > 0 {
   238  			r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
   239  		}
   240  		return h.Log(r)
   241  	})
   242  }
   243  
   244  // FilterHandler returns a Handler that only writes records to the
   245  // wrapped Handler if the given function evaluates true. For example,
   246  // to only log records where the 'err' key is not nil:
   247  //
   248  //    logger.SetHandler(FilterHandler(func(r *Record) bool {
   249  //        for i := 0; i < len(r.Ctx); i += 2 {
   250  //            if r.Ctx[i] == "err" {
   251  //                return r.Ctx[i+1] != nil
   252  //            }
   253  //        }
   254  //        return false
   255  //    }, h))
   256  //
   257  func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
   258  	return FuncHandler(func(r *Record) error {
   259  		if fn(r) {
   260  			return h.Log(r)
   261  		}
   262  		return nil
   263  	})
   264  }
   265  
   266  // MatchFilterHandler returns a Handler that only writes records
   267  // to the wrapped Handler if the given key in the logged
   268  // context matches the value. For example, to only log records
   269  // from your ui package:
   270  //
   271  //    log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
   272  //
   273  func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
   274  	return FilterHandler(func(r *Record) (pass bool) {
   275  		switch key {
   276  		case r.KeyNames.Lvl:
   277  			return r.Lvl == value
   278  		case r.KeyNames.Time:
   279  			return r.Time == value
   280  		case r.KeyNames.Msg:
   281  			return r.Msg == value
   282  		}
   283  
   284  		for i := 0; i < len(r.Ctx); i += 2 {
   285  			if r.Ctx[i] == key {
   286  				return r.Ctx[i+1] == value
   287  			}
   288  		}
   289  		return false
   290  	}, h)
   291  }
   292  
   293  // LvlFilterHandler returns a Handler that only writes
   294  // records which are less than the given verbosity
   295  // level to the wrapped Handler. For example, to only
   296  // log Error/Crit records:
   297  //
   298  //     log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
   299  //
   300  func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
   301  	return FilterHandler(func(r *Record) (pass bool) {
   302  		return r.Lvl <= maxLvl
   303  	}, h)
   304  }
   305  
   306  // MultiHandler dispatches any write to each of its handlers.
   307  // This is useful for writing different types of log information
   308  // to different locations. For example, to log to a file and
   309  // standard error:
   310  //
   311  //     log.MultiHandler(
   312  //         log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
   313  //         log.StderrHandler)
   314  //
   315  func MultiHandler(hs ...Handler) Handler {
   316  	return FuncHandler(func(r *Record) error {
   317  		for _, h := range hs {
   318  			// what to do about failures?
   319  			h.Log(r)
   320  		}
   321  		return nil
   322  	})
   323  }
   324  
   325  // FailoverHandler writes all log records to the first handler
   326  // specified, but will failover and write to the second handler if
   327  // the first handler has failed, and so on for all handlers specified.
   328  // For example you might want to log to a network socket, but failover
   329  // to writing to a file if the network fails, and then to
   330  // standard out if the file write fails:
   331  //
   332  //     log.FailoverHandler(
   333  //         log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
   334  //         log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
   335  //         log.StdoutHandler)
   336  //
   337  // All writes that do not go to the first handler will add context with keys of
   338  // the form "failover_err_{idx}" which explain the error encountered while
   339  // trying to write to the handlers before them in the list.
   340  func FailoverHandler(hs ...Handler) Handler {
   341  	return FuncHandler(func(r *Record) error {
   342  		var err error
   343  		for i, h := range hs {
   344  			err = h.Log(r)
   345  			if err == nil {
   346  				return nil
   347  			}
   348  			r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
   349  		}
   350  
   351  		return err
   352  	})
   353  }
   354  
   355  // ChannelHandler writes all records to the given channel.
   356  // It blocks if the channel is full. Useful for async processing
   357  // of log messages, it's used by BufferedHandler.
   358  func ChannelHandler(recs chan<- *Record) Handler {
   359  	return FuncHandler(func(r *Record) error {
   360  		recs <- r
   361  		return nil
   362  	})
   363  }
   364  
   365  // BufferedHandler writes all records to a buffered
   366  // channel of the given size which flushes into the wrapped
   367  // handler whenever it is available for writing. Since these
   368  // writes happen asynchronously, all writes to a BufferedHandler
   369  // never return an error and any errors from the wrapped handler are ignored.
   370  func BufferedHandler(bufSize int, h Handler) Handler {
   371  	recs := make(chan *Record, bufSize)
   372  	go func() {
   373  		for m := range recs {
   374  			_ = h.Log(m)
   375  		}
   376  	}()
   377  	return ChannelHandler(recs)
   378  }
   379  
   380  // LazyHandler writes all values to the wrapped handler after evaluating
   381  // any lazy functions in the record's context. It is already wrapped
   382  // around StreamHandler and SyslogHandler in this library, you'll only need
   383  // it if you write your own Handler.
   384  func LazyHandler(h Handler) Handler {
   385  	return FuncHandler(func(r *Record) error {
   386  		// go through the values (odd indices) and reassign
   387  		// the values of any lazy fn to the result of its execution
   388  		hadErr := false
   389  		for i := 1; i < len(r.Ctx); i += 2 {
   390  			lz, ok := r.Ctx[i].(Lazy)
   391  			if ok {
   392  				v, err := evaluateLazy(lz)
   393  				if err != nil {
   394  					hadErr = true
   395  					r.Ctx[i] = err
   396  				} else {
   397  					if cs, ok := v.(stack.CallStack); ok {
   398  						v = cs.TrimBelow(r.Call).TrimRuntime()
   399  					}
   400  					r.Ctx[i] = v
   401  				}
   402  			}
   403  		}
   404  
   405  		if hadErr {
   406  			r.Ctx = append(r.Ctx, errorKey, "bad lazy")
   407  		}
   408  
   409  		return h.Log(r)
   410  	})
   411  }
   412  
   413  func evaluateLazy(lz Lazy) (interface{}, error) {
   414  	t := reflect.TypeOf(lz.Fn)
   415  
   416  	if t.Kind() != reflect.Func {
   417  		return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
   418  	}
   419  
   420  	if t.NumIn() > 0 {
   421  		return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
   422  	}
   423  
   424  	if t.NumOut() == 0 {
   425  		return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
   426  	}
   427  
   428  	value := reflect.ValueOf(lz.Fn)
   429  	results := value.Call([]reflect.Value{})
   430  	if len(results) == 1 {
   431  		return results[0].Interface(), nil
   432  	}
   433  	values := make([]interface{}, len(results))
   434  	for i, v := range results {
   435  		values[i] = v.Interface()
   436  	}
   437  	return values, nil
   438  }
   439  
   440  // DiscardHandler reports success for all writes but does nothing.
   441  // It is useful for dynamically disabling logging at runtime via
   442  // a Logger's SetHandler method.
   443  func DiscardHandler() Handler {
   444  	return FuncHandler(func(r *Record) error {
   445  		return nil
   446  	})
   447  }
   448  
   449  // Must provides the following Handler creation functions
   450  // which instead of returning an error parameter only return a Handler
   451  // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
   452  var Must muster
   453  
   454  func must(h Handler, err error) Handler {
   455  	if err != nil {
   456  		panic(err)
   457  	}
   458  	return h
   459  }
   460  
   461  type muster struct{}
   462  
   463  func (m muster) FileHandler(path string, fmtr Format) Handler {
   464  	return must(FileHandler(path, fmtr))
   465  }
   466  
   467  func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
   468  	return must(NetHandler(network, addr, fmtr))
   469  }