🌝

Go 日志:klog

Posted at — Apr 06, 2022

klog 是 K8s 社区维护的 logging 库,支持在程序命令行注册以下 flag1

示例 1:手动/自动输出日志到文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func main() {
	fs := flag.NewFlagSet("klog", flag.ExitOnError)
	// 注册 flag
	klog.InitFlags(fs)
	fs.Set("skip_log_headers", "true")
	// 解析 flag
	fs.Parse(os.Args[1:])
	klog.Info("nice to meet you")
	klog.ErrorS(errors.New("oops"), "noooo")
	// 手动刷新日志记录到文件
	klog.Flush()
	// 或者每 5s 自动 flush
	// time.Sleep(6 * time.Second)
}
1
2
3
4
$ go run main.go --log_file=out.log --logtostderr=false
$ cat out.log
I0406 21:20:32.475227   88398 main.go:15] nice to meet you
E0406 21:20:32.475666   88398 main.go:16] "noooo" err="oops"

示例 2:goroutine 溢出

前面的示例 1 没有停止 Flush 因此会有 groutine leaking 的问题,解决办法是 defer klog.StopFlushDaemon()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func main() {
	klog.InitFlags(nil)

	// By default klog writes to stderr. Setting logtostderr to false makes klog
	// write to a log file.
	flag.Set("logtostderr", "false")
	flag.Set("log_file", "myfile.log")
	flag.Parse()

	// Info writes the first log message. When the first log file is created,
	// a flushDaemon is started to frequently flush bytes to the file.
	klog.Info("nice to meet you")

	// klog won't ever stop this flushDaemon. To exit without leaking a goroutine,
	// the daemon can be stopped manually.
	klog.StopFlushDaemon()

	// After you stopped the flushDaemon, you can still manually flush.
	klog.Info("bye")
	klog.Flush()
}

func TestLeakingFlushDaemon(t *testing.T) {
	// goleak detects leaking goroutines.
	defer goleak.VerifyNone(t)

	// Without calling StopFlushDaemon in main, this test will fail.
	main()
}

示例 3:结构化日志

使用 InfoS、ErrorS 方法

1
2
// I0406 23:42:44.499187    8424 main.go:18] "Pod status updated" pod="kubedns" status="ready"
klog.InfoS("Pod status updated", "pod", "kubedns", "status", "ready")

示例 4:输出到 Buffer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func main() {
	klog.InitFlags(nil)
	flag.Set("logtostderr", "false")
	flag.Set("alsologtostderr", "false")
	flag.Parse()

	buf := new(bytes.Buffer)
	// 接收一个 io.Writer 参数
	klog.SetOutput(buf)
	klog.Info("nice to meet you")
	klog.Flush()

	fmt.Printf("LOGGED: %s", buf.String())
}

示例 4:klogr

klogr 实现了 go-logr/logr 接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
type myError struct {
    str string
}

func (e myError) Error() string {
    return e.str
}

func main() {
    klog.InitFlags(nil)
    flag.Set("v", "3")
    flag.Parse()
    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(myError{"an error occurred"}, "goodbye", "code", -1)
}