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