github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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  // Handler defines where and how log records are written.
    15  // A Logger prints its log records by writing to a Handler.
    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  	Level() Lvl
    21  }
    22  
    23  // FuncHandler returns a Handler that logs records with the given
    24  // function.
    25  func FuncHandler(fn func(r *Record) error, lvl Lvl) Handler {
    26  	return funcHandler{fn, lvl}
    27  }
    28  
    29  type funcHandler struct {
    30  	log func(r *Record) error
    31  	lvl Lvl
    32  }
    33  
    34  func (h funcHandler) Log(r *Record) error {
    35  	return h.log(r)
    36  }
    37  
    38  func (h funcHandler) Level() Lvl {
    39  	return h.lvl
    40  }
    41  
    42  // StreamHandler writes log records to an io.Writer
    43  // with the given format. StreamHandler can be used
    44  // to easily begin writing log records to other
    45  // outputs.
    46  //
    47  // StreamHandler wraps itself with LazyHandler and SyncHandler
    48  // to evaluate Lazy objects and perform safe concurrent writes.
    49  func StreamHandler(wr io.Writer, fmtr Format) Handler {
    50  	h := FuncHandler(func(r *Record) error {
    51  		_, err := wr.Write(fmtr.Format(r))
    52  		return err
    53  	}, LvlTrace)
    54  	return LazyHandler(SyncHandler(h))
    55  }
    56  
    57  // SyncHandler can be wrapped around a handler to guarantee that
    58  // only a single Log operation can proceed at a time. It's necessary
    59  // for thread-safe concurrent writes.
    60  func SyncHandler(h Handler) Handler {
    61  	var mu sync.Mutex
    62  	return FuncHandler(func(r *Record) error {
    63  		defer mu.Unlock()
    64  		mu.Lock()
    65  		return h.Log(r)
    66  	}, h.Level())
    67  }
    68  
    69  // FileHandler returns a handler which writes log records to the give file
    70  // using the given format. If the path
    71  // already exists, FileHandler will append to the given file. If it does not,
    72  // FileHandler will create the file with mode 0644.
    73  func FileHandler(path string, fmtr Format) (Handler, error) {
    74  	f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return closingHandler{f, StreamHandler(f, fmtr)}, nil
    79  }
    80  
    81  // NetHandler opens a socket to the given address and writes records
    82  // over the connection.
    83  func NetHandler(network, addr string, fmtr Format) (Handler, error) {
    84  	conn, err := net.Dial(network, addr)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
    90  }
    91  
    92  // XXX: closingHandler is essentially unused at the moment
    93  // it's meant for a future time when the Handler interface supports
    94  // a possible Close() operation
    95  type closingHandler struct {
    96  	io.WriteCloser
    97  	Handler
    98  }
    99  
   100  func (h *closingHandler) Close() error {
   101  	return h.WriteCloser.Close()
   102  }
   103  
   104  // CallerFileHandler returns a Handler that adds the line number and file of
   105  // the calling function to the context with key "caller".
   106  func CallerFileHandler(h Handler) Handler {
   107  	return FuncHandler(func(r *Record) error {
   108  		r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call))
   109  		return h.Log(r)
   110  	}, h.Level())
   111  }
   112  
   113  // CallerFuncHandler returns a Handler that adds the calling function name to
   114  // the context with key "fn".
   115  func CallerFuncHandler(h Handler) Handler {
   116  	return FuncHandler(func(r *Record) error {
   117  		r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
   118  		return h.Log(r)
   119  	}, h.Level())
   120  }
   121  
   122  // This function is here to please go vet on Go < 1.8.
   123  func formatCall(format string, c stack.Call) string {
   124  	return fmt.Sprintf(format, c)
   125  }
   126  
   127  // CallerStackHandler returns a Handler that adds a stack trace to the context
   128  // with key "stack". The stack trace is formatted as a space separated list of
   129  // call sites inside matching []'s. The most recent call site is listed first.
   130  // Each call site is formatted according to format. See the documentation of
   131  // package github.com/go-stack/stack for the list of supported formats.
   132  func CallerStackHandler(format string, h Handler) Handler {
   133  	return FuncHandler(func(r *Record) error {
   134  		s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
   135  		if len(s) > 0 {
   136  			r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
   137  		}
   138  		return h.Log(r)
   139  	}, h.Level())
   140  }
   141  
   142  // FilterHandler returns a Handler that only writes records to the
   143  // wrapped Handler if the given function evaluates true. For example,
   144  // to only log records where the 'err' key is not nil:
   145  //
   146  //	logger.SetHandler(FilterHandler(func(r *Record) bool {
   147  //	    for i := 0; i < len(r.Ctx); i += 2 {
   148  //	        if r.Ctx[i] == "err" {
   149  //	            return r.Ctx[i+1] != nil
   150  //	        }
   151  //	    }
   152  //	    return false
   153  //	}, h))
   154  func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
   155  	return FuncHandler(func(r *Record) error {
   156  		if fn(r) {
   157  			return h.Log(r)
   158  		}
   159  		return nil
   160  	}, h.Level())
   161  }
   162  
   163  // MatchFilterHandler returns a Handler that only writes records
   164  // to the wrapped Handler if the given key in the logged
   165  // context matches the value. For example, to only log records
   166  // from your ui package:
   167  //
   168  //	log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
   169  func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
   170  	return FilterHandler(func(r *Record) (pass bool) {
   171  		switch key {
   172  		case r.KeyNames.Lvl:
   173  			return r.Lvl == value
   174  		case r.KeyNames.Time:
   175  			return r.Time == value
   176  		case r.KeyNames.Msg:
   177  			return r.Msg == value
   178  		}
   179  
   180  		for i := 0; i < len(r.Ctx); i += 2 {
   181  			if r.Ctx[i] == key {
   182  				return r.Ctx[i+1] == value
   183  			}
   184  		}
   185  		return false
   186  	}, h)
   187  }
   188  
   189  // LvlFilterHandler returns a Handler that only writes
   190  // records which are less than the given verbosity
   191  // level to the wrapped Handler. For example, to only
   192  // log Error/Crit records:
   193  //
   194  //	log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
   195  func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
   196  	return FilterHandler(func(r *Record) (pass bool) {
   197  		return r.Lvl <= maxLvl
   198  	}, h)
   199  }
   200  
   201  // MultiHandler dispatches any write to each of its handlers.
   202  // This is useful for writing different types of log information
   203  // to different locations. For example, to log to a file and
   204  // standard error:
   205  //
   206  //	log.MultiHandler(
   207  //	    log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
   208  //	    log.StderrHandler)
   209  func MultiHandler(hs ...Handler) Handler {
   210  	return FuncHandler(func(r *Record) error {
   211  		for _, h := range hs {
   212  			// what to do about failures?
   213  			h.Log(r)
   214  		}
   215  		return nil
   216  	}, LvlDebug)
   217  }
   218  
   219  // FailoverHandler writes all log records to the first handler
   220  // specified, but will failover and write to the second handler if
   221  // the first handler has failed, and so on for all handlers specified.
   222  // For example you might want to log to a network socket, but failover
   223  // to writing to a file if the network fails, and then to
   224  // standard out if the file write fails:
   225  //
   226  //	log.FailoverHandler(
   227  //	    log.Must.NetHandler("tcp", ":9090", log.JSONFormat()),
   228  //	    log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
   229  //	    log.StdoutHandler)
   230  //
   231  // All writes that do not go to the first handler will add context with keys of
   232  // the form "failover_err_{idx}" which explain the error encountered while
   233  // trying to write to the handlers before them in the list.
   234  func FailoverHandler(hs ...Handler) Handler {
   235  	return FuncHandler(func(r *Record) error {
   236  		var err error
   237  		for i, h := range hs {
   238  			err = h.Log(r)
   239  			if err == nil {
   240  				return nil
   241  			}
   242  			r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
   243  		}
   244  
   245  		return err
   246  	}, LvlTrace)
   247  }
   248  
   249  // ChannelHandler writes all records to the given channel.
   250  // It blocks if the channel is full. Useful for async processing
   251  // of log messages, it's used by BufferedHandler.
   252  func ChannelHandler(recs chan<- *Record, lvl Lvl) Handler {
   253  	return FuncHandler(func(r *Record) error {
   254  		recs <- r
   255  		return nil
   256  	}, lvl)
   257  }
   258  
   259  // BufferedHandler writes all records to a buffered
   260  // channel of the given size which flushes into the wrapped
   261  // handler whenever it is available for writing. Since these
   262  // writes happen asynchronously, all writes to a BufferedHandler
   263  // never return an error and any errors from the wrapped handler are ignored.
   264  func BufferedHandler(bufSize int, h Handler) Handler {
   265  	recs := make(chan *Record, bufSize)
   266  	go func() {
   267  		for m := range recs {
   268  			_ = h.Log(m)
   269  		}
   270  	}()
   271  
   272  	return ChannelHandler(recs, h.Level())
   273  }
   274  
   275  // LazyHandler writes all values to the wrapped handler after evaluating
   276  // any lazy functions in the record's context. It is already wrapped
   277  // around StreamHandler and SyslogHandler in this library, you'll only need
   278  // it if you write your own Handler.
   279  func LazyHandler(h Handler) Handler {
   280  	return FuncHandler(func(r *Record) error {
   281  		// go through the values (odd indices) and reassign
   282  		// the values of any lazy fn to the result of its execution
   283  		hadErr := false
   284  		for i := 1; i < len(r.Ctx); i += 2 {
   285  			lz, ok := r.Ctx[i].(Lazy)
   286  			if ok {
   287  				v, err := evaluateLazy(lz)
   288  				if err != nil {
   289  					hadErr = true
   290  					r.Ctx[i] = err
   291  				} else {
   292  					if cs, ok := v.(stack.CallStack); ok {
   293  						v = cs.TrimBelow(r.Call).TrimRuntime()
   294  					}
   295  					r.Ctx[i] = v
   296  				}
   297  			}
   298  		}
   299  
   300  		if hadErr {
   301  			r.Ctx = append(r.Ctx, errorKey, "bad lazy")
   302  		}
   303  
   304  		return h.Log(r)
   305  	}, h.Level())
   306  }
   307  
   308  func evaluateLazy(lz Lazy) (interface{}, error) {
   309  	t := reflect.TypeOf(lz.Fn)
   310  
   311  	if t.Kind() != reflect.Func {
   312  		return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
   313  	}
   314  
   315  	if t.NumIn() > 0 {
   316  		return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
   317  	}
   318  
   319  	if t.NumOut() == 0 {
   320  		return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
   321  	}
   322  
   323  	value := reflect.ValueOf(lz.Fn)
   324  	results := value.Call([]reflect.Value{})
   325  	if len(results) == 1 {
   326  		return results[0].Interface(), nil
   327  	}
   328  	values := make([]interface{}, len(results))
   329  	for i, v := range results {
   330  		values[i] = v.Interface()
   331  	}
   332  	return values, nil
   333  }
   334  
   335  // DiscardHandler reports success for all writes but does nothing.
   336  // It is useful for dynamically disabling logging at runtime via
   337  // a Logger's SetHandler method.
   338  func DiscardHandler() Handler {
   339  	return FuncHandler(func(r *Record) error {
   340  		return nil
   341  	}, LvlDiscard)
   342  }
   343  
   344  // Must provides the following Handler creation functions
   345  // which instead of returning an error parameter only return a Handler
   346  // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
   347  var Must muster
   348  
   349  func must(h Handler, err error) Handler {
   350  	if err != nil {
   351  		panic(err)
   352  	}
   353  	return h
   354  }
   355  
   356  type muster struct{}
   357  
   358  func (m muster) FileHandler(path string, fmtr Format) Handler {
   359  	return must(FileHandler(path, fmtr))
   360  }
   361  
   362  func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
   363  	return must(NetHandler(network, addr, fmtr))
   364  }