github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/log/logger.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"time"
     7  
     8  	"github.com/go-stack/stack"
     9  )
    10  
    11  const timeKey = "t"
    12  const lvlKey = "lvl"
    13  const msgKey = "msg"
    14  const ctxKey = "ctx"
    15  const errorKey = "LOG15_ERROR"
    16  const skipLevel = 2
    17  
    18  type Lvl int
    19  
    20  const (
    21  	LvlCrit Lvl = iota
    22  	LvlError
    23  	LvlWarn
    24  	LvlInfo
    25  	LvlDebug
    26  	LvlTrace
    27  	LvlDiscard Lvl = -1
    28  )
    29  
    30  // AlignedString returns a 5-character string containing the name of a Lvl.
    31  func (l Lvl) AlignedString() string {
    32  	switch l {
    33  	case LvlTrace:
    34  		return "TRACE"
    35  	case LvlDebug:
    36  		return "DEBUG"
    37  	case LvlInfo:
    38  		return "INFO "
    39  	case LvlWarn:
    40  		return "WARN "
    41  	case LvlError:
    42  		return "ERROR"
    43  	case LvlCrit:
    44  		return "CRIT "
    45  	default:
    46  		panic("bad level")
    47  	}
    48  }
    49  
    50  // Strings returns the name of a Lvl.
    51  func (l Lvl) String() string {
    52  	switch l {
    53  	case LvlTrace:
    54  		return "trce"
    55  	case LvlDebug:
    56  		return "dbug"
    57  	case LvlInfo:
    58  		return "info"
    59  	case LvlWarn:
    60  		return "warn"
    61  	case LvlError:
    62  		return "eror"
    63  	case LvlCrit:
    64  		return "crit"
    65  	default:
    66  		panic("bad level")
    67  	}
    68  }
    69  
    70  // LvlFromString returns the appropriate Lvl from a string name.
    71  // Useful for parsing command line args and configuration files.
    72  func LvlFromString(lvlString string) (Lvl, error) {
    73  	switch lvlString {
    74  	case "trace", "trce":
    75  		return LvlTrace, nil
    76  	case "debug", "dbug":
    77  		return LvlDebug, nil
    78  	case "info":
    79  		return LvlInfo, nil
    80  	case "warn":
    81  		return LvlWarn, nil
    82  	case "error", "eror":
    83  		return LvlError, nil
    84  	case "crit":
    85  		return LvlCrit, nil
    86  	default:
    87  		return LvlDebug, fmt.Errorf("unknown level: %v", lvlString)
    88  	}
    89  }
    90  
    91  // A Record is what a Logger asks its handler to write
    92  type Record struct {
    93  	Time     time.Time
    94  	Lvl      Lvl
    95  	Msg      string
    96  	Ctx      []interface{}
    97  	Call     stack.Call
    98  	KeyNames RecordKeyNames
    99  }
   100  
   101  // RecordKeyNames gets stored in a Record when the write function is executed.
   102  type RecordKeyNames struct {
   103  	Time string
   104  	Msg  string
   105  	Lvl  string
   106  	Ctx  string
   107  }
   108  
   109  type Logging func(msg string, ctx ...interface{})
   110  
   111  // A Logger writes key/value pairs to a Handler
   112  type Logger interface {
   113  	// New returns a new Logger that has this logger's context plus the given context
   114  	New(ctx ...interface{}) Logger
   115  
   116  	// GetHandler gets the handler associated with the logger.
   117  	GetHandler() Handler
   118  
   119  	// SetHandler updates the logger to write records to the specified handler.
   120  	SetHandler(h Handler)
   121  
   122  	// Log a message at the given level with context key/value pairs
   123  	Trace(msg string, ctx ...interface{})
   124  	Debug(msg string, ctx ...interface{})
   125  	Info(msg string, ctx ...interface{})
   126  	Warn(msg string, ctx ...interface{})
   127  	Error(msg string, ctx ...interface{})
   128  	Crit(msg string, ctx ...interface{})
   129  
   130  	OnTrace(func(l Logging))
   131  	OnDebug(func(l Logging))
   132  	OnInfo(func(l Logging))
   133  	OnWarn(func(l Logging))
   134  	OnError(func(l Logging))
   135  	OnCrit(func(l Logging))
   136  }
   137  
   138  type logger struct {
   139  	ctx []interface{}
   140  	h   *swapHandler
   141  }
   142  
   143  func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) {
   144  	if l.h.Level() < lvl {
   145  		return
   146  	}
   147  
   148  	l.h.Log(&Record{
   149  		Time: time.Now(),
   150  		Lvl:  lvl,
   151  		Msg:  msg,
   152  		Ctx:  newContext(l.ctx, ctx),
   153  		Call: stack.Caller(skip),
   154  		KeyNames: RecordKeyNames{
   155  			Time: timeKey,
   156  			Msg:  msgKey,
   157  			Lvl:  lvlKey,
   158  			Ctx:  ctxKey,
   159  		},
   160  	})
   161  }
   162  
   163  func (l *logger) New(ctx ...interface{}) Logger {
   164  	child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
   165  	child.SetHandler(l.h)
   166  	return child
   167  }
   168  
   169  func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
   170  	normalizedSuffix := normalize(suffix)
   171  	newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
   172  	n := copy(newCtx, prefix)
   173  	copy(newCtx[n:], normalizedSuffix)
   174  	return newCtx
   175  }
   176  
   177  func (l *logger) Trace(msg string, ctx ...interface{}) {
   178  	l.write(msg, LvlTrace, ctx, skipLevel)
   179  }
   180  
   181  func (l *logger) Debug(msg string, ctx ...interface{}) {
   182  	l.write(msg, LvlDebug, ctx, skipLevel)
   183  }
   184  
   185  func (l *logger) Info(msg string, ctx ...interface{}) {
   186  	l.write(msg, LvlInfo, ctx, skipLevel)
   187  }
   188  
   189  func (l *logger) Warn(msg string, ctx ...interface{}) {
   190  	l.write(msg, LvlWarn, ctx, skipLevel)
   191  }
   192  
   193  func (l *logger) Error(msg string, ctx ...interface{}) {
   194  	l.write(msg, LvlError, ctx, skipLevel)
   195  }
   196  
   197  func (l *logger) Crit(msg string, ctx ...interface{}) {
   198  	l.write(msg, LvlCrit, ctx, skipLevel)
   199  	os.Exit(1)
   200  }
   201  
   202  func (l *logger) GetHandler() Handler {
   203  	return l.h.Get()
   204  }
   205  
   206  func (l *logger) SetHandler(h Handler) {
   207  	l.h.Swap(h)
   208  }
   209  
   210  func (l *logger) OnTrace(fn func(l Logging)) {
   211  	if l.GetHandler().Level() >= LvlTrace {
   212  		fn(l.Trace)
   213  	}
   214  }
   215  
   216  func (l *logger) OnDebug(fn func(l Logging)) {
   217  	if l.GetHandler().Level() >= LvlDebug {
   218  		fn(l.Debug)
   219  	}
   220  }
   221  func (l *logger) OnInfo(fn func(l Logging)) {
   222  	if l.GetHandler().Level() >= LvlInfo {
   223  		fn(l.Info)
   224  	}
   225  }
   226  func (l *logger) OnWarn(fn func(l Logging)) {
   227  	if l.GetHandler().Level() >= LvlWarn {
   228  		fn(l.Warn)
   229  	}
   230  }
   231  func (l *logger) OnError(fn func(l Logging)) {
   232  	if l.GetHandler().Level() >= LvlError {
   233  		fn(l.Error)
   234  	}
   235  }
   236  func (l *logger) OnCrit(fn func(l Logging)) {
   237  	if l.GetHandler().Level() >= LvlCrit {
   238  		fn(l.Crit)
   239  	}
   240  }
   241  
   242  func normalize(ctx []interface{}) []interface{} {
   243  	// if the caller passed a Ctx object, then expand it
   244  	if len(ctx) == 1 {
   245  		if ctxMap, ok := ctx[0].(Ctx); ok {
   246  			ctx = ctxMap.toArray()
   247  		}
   248  	}
   249  
   250  	// ctx needs to be even because it's a series of key/value pairs
   251  	// no one wants to check for errors on logging functions,
   252  	// so instead of erroring on bad input, we'll just make sure
   253  	// that things are the right length and users can fix bugs
   254  	// when they see the output looks wrong
   255  	if len(ctx)%2 != 0 {
   256  		ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
   257  	}
   258  
   259  	return ctx
   260  }
   261  
   262  // Lazy allows you to defer calculation of a logged value that is expensive
   263  // to compute until it is certain that it must be evaluated with the given filters.
   264  //
   265  // Lazy may also be used in conjunction with a Logger's New() function
   266  // to generate a child logger which always reports the current value of changing
   267  // state.
   268  //
   269  // You may wrap any function which takes no arguments to Lazy. It may return any
   270  // number of values of any type.
   271  type Lazy struct {
   272  	Fn interface{}
   273  }
   274  
   275  // Ctx is a map of key/value pairs to pass as context to a log function
   276  // Use this only if you really need greater safety around the arguments you pass
   277  // to the logging functions.
   278  type Ctx map[string]interface{}
   279  
   280  func (c Ctx) toArray() []interface{} {
   281  	arr := make([]interface{}, len(c)*2)
   282  
   283  	i := 0
   284  	for k, v := range c {
   285  		arr[i] = k
   286  		arr[i+1] = v
   287  		i += 2
   288  	}
   289  
   290  	return arr
   291  }