Home > Software engineering >  How to standardize go-kit/kit/log fields
How to standardize go-kit/kit/log fields

Time:10-20

Problem

go-kit/log Logging interface is straightforward and nice

type Logger interface {
    Log(keyvals ...interface{}) error
}

But people tend to think in different ways and name the same things with different names.

I see in a code that one calls a field for error text as "err" and other "error". This makes logs difficult to search for. You have to search for both "err" and "error" in the same time. This also can be the case for "msg" or "message" fields.

Is there any way to standardize such naming?

CodePudding user response:

I see three options to solve this problem:

  1. A formal convention within a team and a linter to check it
  2. A shared tagging package for log field names declared as consts (example ext package in opentracing-go). A team always use it for Log(ext.Message, "log message text", ext.Error, err) calls.
  3. A syntactic sugar which hides the field naming inside. It can look like this (live example)
    // pakage loghelper
    func Err(err error) []interface{} {
        return []interface{}{"err", err}
    }
    
    func Msg(s string) []interface{} {
        return []interface{}{"msg", s}
    }
    
    func KV(items ...interface{}) []interface{} {
        var kv []interface{}
        for _, item := range items {
            switch v := item.(type) {
            default:
                kv = append(kv, v)
            case []interface{}:
                kv = append(kv, v...)
            }
        }
        return kv
    }
    
    // USAGE

    // package main
    import lh ./loghelper
    cid = "42"
    logger.Log(lh.KV(
      lh.Msg("log message text"), 
      lh.Err(errors.New("error-test")), 
      "customer.id", cid
    )...)

CodePudding user response:

I'd find a builder pattern cleaner and more readable:

type KVBuilder struct {
    KeyVals []interface{}
}

func NewKVBuilder() *KVBuilder {
    return &KVBuilder{}
}

func (k *KVBuilder) Err(err error) *KVBuilder {
    return k.KV("err", err)
}

func (k *KVBuilder) Msg(msg string) *KVBuilder {
    return k.KV("msg", msg)
}

func (k *KVBuilder) KV(kv ...interface{}) *KVBuilder {
    k.KeyVals = append(k.KeyVals, kv...)
    return k
}

Using it:

var logger Logger

logger.Log(NewKVBuilder().
    Err(errors.New("foo")).
    Msg("bar").
    KV("some", "other").
    KeyVals...,
)

And if you add another method to KVBuilder:

func (k *KVBuilder) LogTo(logger Logger) error {
    return logger.Log(k.KeyVals...)
}

You can also use it like this:

NewKVBuilder().
    Err(errors.New("foo")).
    Msg("bar").
    KV("some", "other").
    LogTo(logger)
  • Related