123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- package log
- import (
- "fmt"
- "os"
- "time"
- "github.com/go-stack/stack"
- )
- const timeKey = "t"
- const lvlKey = "lvl"
- const msgKey = "msg"
- const errorKey = "LOG15_ERROR"
- type Lvl int
- const (
- LvlCrit Lvl = iota
- LvlError
- LvlWarn
- LvlInfo
- LvlDebug
- LvlTrace
- )
- // AlignedString returns a 5-character string containing the name of a Lvl.
- func (l Lvl) AlignedString() string {
- switch l {
- case LvlTrace:
- return "TRACE"
- case LvlDebug:
- return "DEBUG"
- case LvlInfo:
- return "INFO "
- case LvlWarn:
- return "WARN "
- case LvlError:
- return "ERROR"
- case LvlCrit:
- return "CRIT "
- default:
- panic("bad level")
- }
- }
- // Strings returns the name of a Lvl.
- func (l Lvl) String() string {
- switch l {
- case LvlTrace:
- return "trce"
- case LvlDebug:
- return "dbug"
- case LvlInfo:
- return "info"
- case LvlWarn:
- return "warn"
- case LvlError:
- return "eror"
- case LvlCrit:
- return "crit"
- default:
- panic("bad level")
- }
- }
- // LvlFromString returns the appropriate Lvl from a string name.
- // Useful for parsing command line args and configuration files.
- func LvlFromString(lvlString string) (Lvl, error) {
- switch lvlString {
- case "trace", "trce":
- return LvlTrace, nil
- case "debug", "dbug":
- return LvlDebug, nil
- case "info":
- return LvlInfo, nil
- case "warn":
- return LvlWarn, nil
- case "error", "eror":
- return LvlError, nil
- case "crit":
- return LvlCrit, nil
- default:
- return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString)
- }
- }
- // A Record is what a Logger asks its handler to write
- type Record struct {
- Time time.Time
- Lvl Lvl
- Msg string
- Ctx []interface{}
- Call stack.Call
- KeyNames RecordKeyNames
- }
- // RecordKeyNames gets stored in a Record when the write function is executed.
- type RecordKeyNames struct {
- Time string
- Msg string
- Lvl string
- }
- // A Logger writes key/value pairs to a Handler
- type Logger interface {
- // New returns a new Logger that has this logger's context plus the given context
- New(ctx ...interface{}) Logger
- // GetHandler gets the handler associated with the logger.
- GetHandler() Handler
- // SetHandler updates the logger to write records to the specified handler.
- SetHandler(h Handler)
- // Log a message at the given level with context key/value pairs
- Trace(msg string, ctx ...interface{})
- Debug(msg string, ctx ...interface{})
- Info(msg string, ctx ...interface{})
- Warn(msg string, ctx ...interface{})
- Error(msg string, ctx ...interface{})
- Crit(msg string, ctx ...interface{})
- }
- type logger struct {
- ctx []interface{}
- h *swapHandler
- }
- func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) {
- l.h.Log(&Record{
- Time: time.Now(),
- Lvl: lvl,
- Msg: msg,
- Ctx: newContext(l.ctx, ctx),
- Call: stack.Caller(2),
- KeyNames: RecordKeyNames{
- Time: timeKey,
- Msg: msgKey,
- Lvl: lvlKey,
- },
- })
- }
- func (l *logger) New(ctx ...interface{}) Logger {
- child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
- child.SetHandler(l.h)
- return child
- }
- func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
- normalizedSuffix := normalize(suffix)
- newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
- n := copy(newCtx, prefix)
- copy(newCtx[n:], normalizedSuffix)
- return newCtx
- }
- func (l *logger) Trace(msg string, ctx ...interface{}) {
- l.write(msg, LvlTrace, ctx)
- }
- func (l *logger) Debug(msg string, ctx ...interface{}) {
- l.write(msg, LvlDebug, ctx)
- }
- func (l *logger) Info(msg string, ctx ...interface{}) {
- l.write(msg, LvlInfo, ctx)
- }
- func (l *logger) Warn(msg string, ctx ...interface{}) {
- l.write(msg, LvlWarn, ctx)
- }
- func (l *logger) Error(msg string, ctx ...interface{}) {
- l.write(msg, LvlError, ctx)
- }
- func (l *logger) Crit(msg string, ctx ...interface{}) {
- l.write(msg, LvlCrit, ctx)
- os.Exit(1)
- }
- func (l *logger) GetHandler() Handler {
- return l.h.Get()
- }
- func (l *logger) SetHandler(h Handler) {
- l.h.Swap(h)
- }
- func normalize(ctx []interface{}) []interface{} {
- // if the caller passed a Ctx object, then expand it
- if len(ctx) == 1 {
- if ctxMap, ok := ctx[0].(Ctx); ok {
- ctx = ctxMap.toArray()
- }
- }
- // ctx needs to be even because it's a series of key/value pairs
- // no one wants to check for errors on logging functions,
- // so instead of erroring on bad input, we'll just make sure
- // that things are the right length and users can fix bugs
- // when they see the output looks wrong
- if len(ctx)%2 != 0 {
- ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
- }
- return ctx
- }
- // Lazy allows you to defer calculation of a logged value that is expensive
- // to compute until it is certain that it must be evaluated with the given filters.
- //
- // Lazy may also be used in conjunction with a Logger's New() function
- // to generate a child logger which always reports the current value of changing
- // state.
- //
- // You may wrap any function which takes no arguments to Lazy. It may return any
- // number of values of any type.
- type Lazy struct {
- Fn interface{}
- }
- // Ctx is a map of key/value pairs to pass as context to a log function
- // Use this only if you really need greater safety around the arguments you pass
- // to the logging functions.
- type Ctx map[string]interface{}
- func (c Ctx) toArray() []interface{} {
- arr := make([]interface{}, len(c)*2)
- i := 0
- for k, v := range c {
- arr[i] = k
- arr[i+1] = v
- i += 2
- }
- return arr
- }
|