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