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