github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/chain/log/handler.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"reflect"
     9  	"runtime"
    10  	"sync"
    11  
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/go-stack/stack"
    16  )
    17  
    18  // Handler defines where and how log records are written.
    19  // A Logger prints its log records by writing to a Handler.
    20  // Handlers are composable, providing you great flexibility in combining
    21  // them to achieve the logging structure that suits your applications.
    22  type Handler interface {
    23  	Log(r *Record) error
    24  }
    25  
    26  // FuncHandler returns a Handler that logs records with the given
    27  // function.
    28  func FuncHandler(fn func(r *Record) error) Handler {
    29  	return funcHandler(fn)
    30  }
    31  
    32  type funcHandler func(r *Record) error
    33  
    34  func (h funcHandler) Log(r *Record) error {
    35  	return h(r)
    36  }
    37  
    38  // StreamHandler writes log records to an io.Writer
    39  // with the given format. StreamHandler can be used
    40  // to easily begin writing log records to other
    41  // outputs.
    42  //
    43  // StreamHandler wraps itself with LazyHandler and SyncHandler
    44  // to evaluate Lazy objects and perform safe concurrent writes.
    45  func StreamHandler(wr io.Writer, fmtr Format) Handler {
    46  	h := FuncHandler(func(r *Record) error {
    47  		_, err := wr.Write(fmtr.Format(r))
    48  		return err
    49  	})
    50  	return LazyHandler(SyncHandler(h))
    51  }
    52  
    53  // SyncHandler can be wrapped around a handler to guarantee that
    54  // only a single Log operation can proceed at a time. It's necessary
    55  // for thread-safe concurrent writes.
    56  func SyncHandler(h Handler) Handler {
    57  	var mu sync.Mutex
    58  	return FuncHandler(func(r *Record) error {
    59  		defer mu.Unlock()
    60  		mu.Lock()
    61  		return h.Log(r)
    62  	})
    63  }
    64  
    65  // FileHandler returns a handler which writes log records to the give file
    66  // using the given format. If the path
    67  // already exists, FileHandler will append to the given file. If it does not,
    68  // FileHandler will create the file with mode 0644.
    69  func FileHandler(path string, fmtr Format) (Handler, error) {
    70  	f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	return closingHandler{f, StreamHandler(f, fmtr)}, nil
    75  }
    76  
    77  // countingWriter wraps a WriteCloser object in order to count the written bytes.
    78  type countingWriter struct {
    79  	sync.RWMutex                // read write locker for rotate log file
    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  
   128  	var ns int64
   129  	if runtime.GOOS == "windows" || cut == 0 {
   130  		// Do not truncate file in windows, as append flag will cause Access Denied error
   131  		ns = fi.Size()
   132  	} else {
   133  		ns = fi.Size() - cut
   134  		if err = f.Truncate(ns); err != nil {
   135  			return nil, err
   136  		}
   137  	}
   138  
   139  	return &countingWriter{w: f, count: uint(ns)}, nil
   140  }
   141  
   142  // RotatingFileHandler returns a handler which writes log records to file chunks
   143  // at the given path. When a file's size reaches the limit, the handler creates
   144  // a new file named after the timestamp of the first log record it will contain.
   145  func RotatingFileHandler(path string, limit uint, formatter Format) (Handler, error) {
   146  	if err := os.MkdirAll(path, 0700); err != nil {
   147  		return nil, err
   148  	}
   149  	/*
   150  		files, err := ioutil.ReadDir(path)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  		re := regexp.MustCompile(`\.log$`)
   155  		last := len(files) - 1
   156  		for last >= 0 && (!files[last].Mode().IsRegular() || !re.MatchString(files[last].Name())) {
   157  			last--
   158  		}
   159  		var counter *countingWriter
   160  		if last >= 0 && files[last].Size() < int64(limit) {
   161  			// Open the last file, and continue to write into it until it's size reaches the limit.
   162  			if counter, err = prepFile(filepath.Join(path, files[last].Name())); err != nil {
   163  				return nil, err
   164  			}
   165  		}
   166  		if counter == nil {
   167  			counter = new(countingWriter)
   168  		}*/
   169  	counter := new(countingWriter)
   170  	h := StreamHandler(counter, formatter)
   171  
   172  	return FuncHandler(func(r *Record) error {
   173  		if counter.count > limit || counter.w == nil {
   174  			// Hold Write Lock to Reset the Log Counter
   175  			counter.Lock()
   176  			if counter.count > limit {
   177  				counter.Close()
   178  				counter.w = nil
   179  			}
   180  
   181  			// Hold Write Lock to Create a new log file
   182  			if counter.w == nil {
   183  				f, err := os.OpenFile(
   184  					filepath.Join(path, fmt.Sprintf("%s.log", strings.Replace(r.Time.Format("2006-01-02_150405.000"), ".", "_", 1))),
   185  					os.O_CREATE|os.O_APPEND|os.O_WRONLY,
   186  					0600,
   187  				)
   188  				if err != nil {
   189  					return err
   190  				}
   191  				counter.w = f
   192  				counter.count = 0
   193  			}
   194  			counter.Unlock()
   195  		}
   196  
   197  		counter.RLock()
   198  		defer counter.RUnlock()
   199  
   200  		return h.Log(r)
   201  	}), nil
   202  }
   203  
   204  // NetHandler opens a socket to the given address and writes records
   205  // over the connection.
   206  func NetHandler(network, addr string, fmtr Format) (Handler, error) {
   207  	conn, err := net.Dial(network, addr)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
   213  }
   214  
   215  // XXX: closingHandler is essentially unused at the moment
   216  // it's meant for a future time when the Handler interface supports
   217  // a possible Close() operation
   218  type closingHandler struct {
   219  	io.WriteCloser
   220  	Handler
   221  }
   222  
   223  func (h *closingHandler) Close() error {
   224  	return h.WriteCloser.Close()
   225  }
   226  
   227  // CallerFileHandler returns a Handler that adds the line number and file of
   228  // the calling function to the context with key "caller".
   229  func CallerFileHandler(h Handler) Handler {
   230  	return FuncHandler(func(r *Record) error {
   231  		r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call))
   232  		return h.Log(r)
   233  	})
   234  }
   235  
   236  // CallerFuncHandler returns a Handler that adds the calling function name to
   237  // the context with key "fn".
   238  func CallerFuncHandler(h Handler) Handler {
   239  	return FuncHandler(func(r *Record) error {
   240  		r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
   241  		return h.Log(r)
   242  	})
   243  }
   244  
   245  // This function is here to please go vet on Go < 1.8.
   246  func formatCall(format string, c stack.Call) string {
   247  	return fmt.Sprintf(format, c)
   248  }
   249  
   250  // CallerStackHandler returns a Handler that adds a stack trace to the context
   251  // with key "stack". The stack trace is formated as a space separated list of
   252  // call sites inside matching []'s. The most recent call site is listed first.
   253  // Each call site is formatted according to format. See the documentation of
   254  // package github.com/go-stack/stack for the list of supported formats.
   255  func CallerStackHandler(format string, h Handler) Handler {
   256  	return FuncHandler(func(r *Record) error {
   257  		s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
   258  		if len(s) > 0 {
   259  			r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
   260  		}
   261  		return h.Log(r)
   262  	})
   263  }
   264  
   265  // FilterHandler returns a Handler that only writes records to the
   266  // wrapped Handler if the given function evaluates true. For example,
   267  // to only log records where the 'err' key is not nil:
   268  //
   269  //    logger.SetHandler(FilterHandler(func(r *Record) bool {
   270  //        for i := 0; i < len(r.Ctx); i += 2 {
   271  //            if r.Ctx[i] == "err" {
   272  //                return r.Ctx[i+1] != nil
   273  //            }
   274  //        }
   275  //        return false
   276  //    }, h))
   277  //
   278  func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
   279  	return FuncHandler(func(r *Record) error {
   280  		if fn(r) {
   281  			return h.Log(r)
   282  		}
   283  		return nil
   284  	})
   285  }
   286  
   287  // MatchFilterHandler returns a Handler that only writes records
   288  // to the wrapped Handler if the given key in the logged
   289  // context matches the value. For example, to only log records
   290  // from your ui package:
   291  //
   292  //    log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
   293  //
   294  func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
   295  	return FilterHandler(func(r *Record) (pass bool) {
   296  		switch key {
   297  		case r.KeyNames.Lvl:
   298  			return r.Lvl == value
   299  		case r.KeyNames.Time:
   300  			return r.Time == value
   301  		case r.KeyNames.Msg:
   302  			return r.Msg == value
   303  		}
   304  
   305  		for i := 0; i < len(r.Ctx); i += 2 {
   306  			if r.Ctx[i] == key {
   307  				return r.Ctx[i+1] == value
   308  			}
   309  		}
   310  		return false
   311  	}, h)
   312  }
   313  
   314  // LvlFilterHandler returns a Handler that only writes
   315  // records which are less than the given verbosity
   316  // level to the wrapped Handler. For example, to only
   317  // log Error/Crit records:
   318  //
   319  //     log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
   320  //
   321  func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
   322  	return FilterHandler(func(r *Record) (pass bool) {
   323  		return r.Lvl <= maxLvl
   324  	}, h)
   325  }
   326  
   327  // MultiHandler dispatches any write to each of its handlers.
   328  // This is useful for writing different types of log information
   329  // to different locations. For example, to log to a file and
   330  // standard error:
   331  //
   332  //     log.MultiHandler(
   333  //         log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
   334  //         log.StderrHandler)
   335  //
   336  func MultiHandler(hs ...Handler) Handler {
   337  	return FuncHandler(func(r *Record) error {
   338  		for _, h := range hs {
   339  			// what to do about failures?
   340  			h.Log(r)
   341  		}
   342  		return nil
   343  	})
   344  }
   345  
   346  // FailoverHandler writes all log records to the first handler
   347  // specified, but will failover and write to the second handler if
   348  // the first handler has failed, and so on for all handlers specified.
   349  // For example you might want to log to a network socket, but failover
   350  // to writing to a file if the network fails, and then to
   351  // standard out if the file write fails:
   352  //
   353  //     log.FailoverHandler(
   354  //         log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
   355  //         log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
   356  //         log.StdoutHandler)
   357  //
   358  // All writes that do not go to the first handler will add context with keys of
   359  // the form "failover_err_{idx}" which explain the error encountered while
   360  // trying to write to the handlers before them in the list.
   361  func FailoverHandler(hs ...Handler) Handler {
   362  	return FuncHandler(func(r *Record) error {
   363  		var err error
   364  		for i, h := range hs {
   365  			err = h.Log(r)
   366  			if err == nil {
   367  				return nil
   368  			}
   369  			r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
   370  		}
   371  
   372  		return err
   373  	})
   374  }
   375  
   376  // ChannelHandler writes all records to the given channel.
   377  // It blocks if the channel is full. Useful for async processing
   378  // of log messages, it's used by BufferedHandler.
   379  func ChannelHandler(recs chan<- *Record) Handler {
   380  	return FuncHandler(func(r *Record) error {
   381  		recs <- r
   382  		return nil
   383  	})
   384  }
   385  
   386  // BufferedHandler writes all records to a buffered
   387  // channel of the given size which flushes into the wrapped
   388  // handler whenever it is available for writing. Since these
   389  // writes happen asynchronously, all writes to a BufferedHandler
   390  // never return an error and any errors from the wrapped handler are ignored.
   391  func BufferedHandler(bufSize int, h Handler) Handler {
   392  	recs := make(chan *Record, bufSize)
   393  	go func() {
   394  		for m := range recs {
   395  			_ = h.Log(m)
   396  		}
   397  	}()
   398  	return ChannelHandler(recs)
   399  }
   400  
   401  // LazyHandler writes all values to the wrapped handler after evaluating
   402  // any lazy functions in the record's context. It is already wrapped
   403  // around StreamHandler and SyslogHandler in this library, you'll only need
   404  // it if you write your own Handler.
   405  func LazyHandler(h Handler) Handler {
   406  	return FuncHandler(func(r *Record) error {
   407  		// go through the values (odd indices) and reassign
   408  		// the values of any lazy fn to the result of its execution
   409  		hadErr := false
   410  		for i := 1; i < len(r.Ctx); i += 2 {
   411  			lz, ok := r.Ctx[i].(Lazy)
   412  			if ok {
   413  				v, err := evaluateLazy(lz)
   414  				if err != nil {
   415  					hadErr = true
   416  					r.Ctx[i] = err
   417  				} else {
   418  					if cs, ok := v.(stack.CallStack); ok {
   419  						v = cs.TrimBelow(r.Call).TrimRuntime()
   420  					}
   421  					r.Ctx[i] = v
   422  				}
   423  			}
   424  		}
   425  
   426  		if hadErr {
   427  			r.Ctx = append(r.Ctx, errorKey, "bad lazy")
   428  		}
   429  
   430  		return h.Log(r)
   431  	})
   432  }
   433  
   434  func evaluateLazy(lz Lazy) (interface{}, error) {
   435  	t := reflect.TypeOf(lz.Fn)
   436  
   437  	if t.Kind() != reflect.Func {
   438  		return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
   439  	}
   440  
   441  	if t.NumIn() > 0 {
   442  		return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
   443  	}
   444  
   445  	if t.NumOut() == 0 {
   446  		return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
   447  	}
   448  
   449  	value := reflect.ValueOf(lz.Fn)
   450  	results := value.Call([]reflect.Value{})
   451  	if len(results) == 1 {
   452  		return results[0].Interface(), nil
   453  	}
   454  	values := make([]interface{}, len(results))
   455  	for i, v := range results {
   456  		values[i] = v.Interface()
   457  	}
   458  	return values, nil
   459  }
   460  
   461  // DiscardHandler reports success for all writes but does nothing.
   462  // It is useful for dynamically disabling logging at runtime via
   463  // a Logger's SetHandler method.
   464  func DiscardHandler() Handler {
   465  	return FuncHandler(func(r *Record) error {
   466  		return nil
   467  	})
   468  }
   469  
   470  // Must provides the following Handler creation functions
   471  // which instead of returning an error parameter only return a Handler
   472  // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
   473  var Must muster
   474  
   475  func must(h Handler, err error) Handler {
   476  	if err != nil {
   477  		panic(err)
   478  	}
   479  	return h
   480  }
   481  
   482  type muster struct{}
   483  
   484  func (m muster) FileHandler(path string, fmtr Format) Handler {
   485  	return must(FileHandler(path, fmtr))
   486  }
   487  
   488  func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
   489  	return must(NetHandler(network, addr, fmtr))
   490  }