gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/common/log/handler.go (about)

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