关于golang:聊聊klog的klogger

58次阅读

共计 5556 个字符,预计需要花费 14 分钟才能阅读完成。

本文次要钻研一下 klog 的 klogger

logr

go-logr/logr@v0.2.0/logr.go

// Logger represents the ability to log messages, both errors and not.
type Logger interface {
    // Enabled tests whether this Logger is enabled.  For example, commandline
    // flags might be used to set the logging verbosity and disable some info
    // logs.
    Enabled() bool

    // Info logs a non-error message with the given key/value pairs as context.
    //
    // The msg argument should be used to add some constant description to
    // the log line.  The key/value pairs can then be used to add additional
    // variable information.  The key/value pairs should alternate string
    // keys and arbitrary values.
    Info(msg string, keysAndValues ...interface{})

    // Error logs an error, with the given message and key/value pairs as context.
    // It functions similarly to calling Info with the "error" named value, but may
    // have unique behavior, and should be preferred for logging errors (see the
    // package documentations for more information).
    //
    // The msg field should be used to add context to any underlying error,
    // while the err field should be used to attach the actual error that
    // triggered this log line, if present.
    Error(err error, msg string, keysAndValues ...interface{})

    // V returns an Logger value for a specific verbosity level, relative to
    // this Logger.  In other words, V values are additive.  V higher verbosity
    // level means a log message is less important.  It's illegal to pass a log
    // level less than zero.
    V(level int) Logger

    // WithValues adds some key-value pairs of context to a logger.
    // See Info for documentation on how key/value pairs work.
    WithValues(keysAndValues ...interface{}) Logger

    // WithName adds a new element to the logger's name.
    // Successive calls with WithName continue to append
    // suffixes to the logger's name.  It's strongly reccomended
    // that name segments contain only letters, digits, and hyphens
    // (see the package documentation for more information).
    WithName(name string) Logger
}

logr 相似 java 的 slf4j,其 Logger 接口定义了 Enabled、Info、Error、V、WithValues、WithName 办法

klogr

k8s.io/klog/v2@v2.4.0/klogr/klogr.go

type klogger struct {
    level  int
    prefix string
    values []interface{}
}

// New returns a logr.Logger which is implemented by klog.
func New() logr.Logger {
    return klogger{
        level:  0,
        prefix: "",
        values: nil,
    }
}

func (l klogger) Enabled() bool {return bool(klog.V(klog.Level(l.level)).Enabled())
}

func (l klogger) Info(msg string, kvList ...interface{}) {if l.Enabled() {msgStr := flatten("msg", msg)
        trimmed := trimDuplicates(l.values, kvList)
        fixedStr := flatten(trimmed[0]...)
        userStr := flatten(trimmed[1]...)
        klog.InfoDepth(framesToCaller(), l.prefix, "", msgStr," ", fixedStr," ", userStr)
    }
}

func (l klogger) Error(err error, msg string, kvList ...interface{}) {msgStr := flatten("msg", msg)
    var loggableErr interface{}
    if err != nil {loggableErr = err.Error()
    }
    errStr := flatten("error", loggableErr)
    trimmed := trimDuplicates(l.values, kvList)
    fixedStr := flatten(trimmed[0]...)
    userStr := flatten(trimmed[1]...)
    klog.ErrorDepth(framesToCaller(), l.prefix, "", msgStr," ", errStr," ", fixedStr," ", userStr)
}

func (l klogger) V(level int) logr.Logger {new := l.clone()
    new.level = level
    return new
}

func (l klogger) WithValues(kvList ...interface{}) logr.Logger {new := l.clone()
    new.values = append(new.values, kvList...)
    return new
}

func (l klogger) WithName(name string) logr.Logger {new := l.clone()
    if len(l.prefix) > 0 {new.prefix = l.prefix + "/"}
    new.prefix += name
    return new
}

klogger 定义了 level、prefix、values 属性,New 办法创立的 klogger 其 level 为 0;它实现了 logr.Logger 接口,其 Info 先判断 Enabled,之后调用 klog.InfoDepth;其 Error 办法调用的是 klog.ErrorDepth;其 V 办法先进行 clone 在对 level 进行赋值,返回新的 logr.Logger;WithValues 办法先进行 clone,而后设置 values,返回新的 logr.Logger;WithName 办法也是先进行 clone,在设置 prefix,返回新的 logr.Logger

clone

k8s.io/klog/v2@v2.4.0/klogr/klogr.go

func (l klogger) clone() klogger {
    return klogger{
        level:  l.level,
        prefix: l.prefix,
        values: copySlice(l.values),
    }
}

clone 办法应用原有的 klogger 的 level、prefix、values 创立一个新的 klogger

klog.V

k8s.io/klog/v2@v2.4.0/klogr/klogr.go

func V(level Level) Verbose {
    // This function tries hard to be cheap unless there's work to do.
    // The fast path is two atomic loads and compares.

    // Here is a cheap but safe test to see if V logging is enabled globally.
    if logging.verbosity.get() >= level {return newVerbose(level, true)
    }

    // It's off globally but vmodule may still be set.
    // Here is another cheap but safe test to see if vmodule is enabled.
    if atomic.LoadInt32(&logging.filterLength) > 0 {
        // Now we need a proper lock to use the logging structure. The pcs field
        // is shared so we must lock before accessing it. This is fairly expensive,
        // but if V logging is enabled we're slow anyway.
        logging.mu.Lock()
        defer logging.mu.Unlock()
        if runtime.Callers(2, logging.pcs[:]) == 0 {return newVerbose(level, false)
        }
        v, ok := logging.vmap[logging.pcs[0]]
        if !ok {v = logging.setV(logging.pcs[0])
        }
        return newVerbose(level, v >= level)
    }
    return newVerbose(level, false)
}

func newVerbose(level Level, b bool) Verbose {
    if logging.logr == nil {return Verbose{b, nil, logging.filter}
    }
    return Verbose{b, logging.logr.V(int(level)), logging.filter}
}

// Enabled will return true if this log level is enabled, guarded by the value
// of v.
// See the documentation of V for usage.
func (v Verbose) Enabled() bool {return v.enabled}

通过 logging.verbosity 与传入的 level 进行判断,若大于等于则其 enabled 为 true;klogr.New() 默认的 level 为 0

实例

func logrDemo() {log := klogr.New().WithName("MyName").WithValues("user", "you")
    log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
    log.V(3).Info("nice to meet you")
    log.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
    log.Error(errors.New("an error occurred"), "goodbye", "code", -1)
}

输入

I1230 23:12:33.388829    2890 klog_demo.go:42] MyName "msg"="hello" "user"="you" "val1"=1 "val2"={"k":1}
E1230 23:12:33.389032    2890 klog_demo.go:44] MyName "msg"="uh oh" "error"=null "user"="you" "reasons"=[0.1,0.11,3.14] "trouble"=true
E1230 23:12:33.389055    2890 klog_demo.go:45] MyName "msg"="goodbye" "error"="an error occurred" "user"="you" "code"=-1

小结

klogger 定义了 level、prefix、values 属性,New 办法创立的 klogger 其 level 为 0;它实现了 logr.Logger 接口,其 Info 先判断 Enabled,之后调用 klog.InfoDepth;其 Error 办法调用的是 klog.ErrorDepth;其 V 办法先进行 clone 在对 level 进行赋值,返回新的 logr.Logger;WithValues 办法先进行 clone,而后设置 values,返回新的 logr.Logger;WithName 办法也是先进行 clone,在设置 prefix,返回新的 logr.Logger

doc

  • klog
  • logr

正文完
 0