github.com/theQRL/go-zond@v0.1.1/log/handler.go (about)

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