Skip to content

Commit

Permalink
add structured logs function InfoS and ErrorS
Browse files Browse the repository at this point in the history
add InfoS and ErrorS func comment, add InfoS func to Verbose
reduce map test case to avoid go1.11.* maps string order
add kvlistFormat unit test
assume key shoud be string, remove some unsed test case

rename some variable, and refactor test case by review comment
  • Loading branch information
yuzhiquan committed Apr 3, 2020
1 parent db91925 commit bf9f969
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 0 deletions.
84 changes: 84 additions & 0 deletions klog.go
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,53 @@ func (l *loggingT) printWithFileLine(s severity, logr logr.InfoLogger, file stri
l.output(s, logr, buf, file, line, alsoToStderr)
}

// printS if loggr is specified, no need to output with logging module. If
// err arguments is specified, will call logr.Error, or output to errorLog severity
func (l *loggingT) printS(err error, loggr logr.Logger, msg string, keysAndValues ...interface{}) {
if loggr != nil {
if err != nil {
loggr.Error(err, msg, keysAndValues)
} else {
loggr.Info(msg, keysAndValues)
}
return
}
b := &bytes.Buffer{}
b.WriteString(fmt.Sprintf("%q", msg))
if err != nil {
b.WriteByte(' ')
b.WriteString(fmt.Sprintf("err=%q", err.Error()))
}
kvListFormat(b, keysAndValues...)
var s severity
if err == nil {
s = infoLog
} else {
s = errorLog
}
l.printDepth(s, logging.logr, 1, b)
}

const missingValue = "(MISSING)"

func kvListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
for i := 0; i < len(keysAndValues); i += 2 {
var v interface{}
k := keysAndValues[i]
if i+1 < len(keysAndValues) {
v = keysAndValues[i+1]
} else {
v = missingValue
}
b.WriteByte(' ')
if _, ok := v.(fmt.Stringer); ok {
b.WriteString(fmt.Sprintf("%s=%q", k, v))
} else {
b.WriteString(fmt.Sprintf("%s=%#v", k, v))
}
}
}

// redirectBuffer is used to set an alternate destination for the logs
type redirectBuffer struct {
w io.Writer
Expand Down Expand Up @@ -1241,6 +1288,18 @@ func (v Verbose) Infof(format string, args ...interface{}) {
}
}

// InfoS is equivalent to the global InfoS function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) {
if v.enabled {
if v.logr != nil {
v.logr.Info(msg, keysAndValues)
return
}
logging.printS(nil, nil, msg, keysAndValues...)
}
}

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Info(args ...interface{}) {
Expand All @@ -1265,6 +1324,18 @@ func Infof(format string, args ...interface{}) {
logging.printf(infoLog, logging.logr, format, args...)
}

// InfoS structured logs to the INFO log.
// The msg argument used to add constant description to the log line.
// The key/value pairs would be join by "=" ; a newline is always appended.
//
// Basic examples:
// >> klog.InfoS("Pod status updated", "pod", "kubedns", "status", "ready")
// output:
// >> I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated" pod="kubedns" status="ready"
func InfoS(msg string, keysAndValues ...interface{}) {
logging.printS(nil, logging.logr, msg, keysAndValues...)
}

// Warning logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Warning(args ...interface{}) {
Expand Down Expand Up @@ -1313,6 +1384,19 @@ func Errorf(format string, args ...interface{}) {
logging.printf(errorLog, logging.logr, format, args...)
}

// ErrorS structured logs to the ERROR, WARNING, and INFO logs.
// the err argument used as "err" field of log line.
// The msg argument used to add constant description to the log line.
// The key/value pairs would be join by "=" ; a newline is always appended.
//
// Basic examples:
// >> klog.ErrorS(err, "Failed to update pod status")
// output:
// >> E1025 00:15:15.525108 1 controller_utils.go:114] "Failed to update pod status" err="timeout"
func ErrorS(err error, msg string, keysAndValues ...interface{}) {
logging.printS(err, logging.logr, msg, keysAndValues...)
}

// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
Expand Down
122 changes: 122 additions & 0 deletions klog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -753,3 +753,125 @@ func TestKRef(t *testing.T) {
})
}
}

// Test that InfoS works as advertised.
func TestInfoS(t *testing.T) {
setFlags()
defer logging.swap(logging.newBuffers())
timeNow = func() time.Time {
return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
}
pid = 1234
var testDataInfo = []struct {
msg string
format string
keysValues []interface{}
}{
{
msg: "test",
format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" pod=\"kubedns\"\n",
keysValues: []interface{}{"pod", "kubedns"},
},
{
msg: "test",
format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" replicaNum=20\n",
keysValues: []interface{}{"replicaNum", 20},
},
}

for _, data := range testDataInfo {
logging.file[infoLog] = &flushBuffer{}
InfoS(data.msg, data.keysValues...)
var line int
n, err := fmt.Sscanf(contents(infoLog), data.format, &line)
if n != 1 || err != nil {
t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
}
want := fmt.Sprintf(data.format, line)
if contents(infoLog) != want {
t.Errorf("InfoS has wrong format: \n got:\t%s\nwant:\t%s", contents(infoLog), want)
}
}
}

// Test that ErrorS works as advertised.
func TestErrorS(t *testing.T) {
setFlags()
defer logging.swap(logging.newBuffers())
timeNow = func() time.Time {
return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
}
logging.logFile = ""
pid = 1234
ErrorS(fmt.Errorf("update status failed"), "Failed to update pod status", "pod", "kubedns")
var line int
format := "E0102 15:04:05.067890 1234 klog_test.go:%d] \"Failed to update pod status\" err=\"update status failed\" pod=\"kubedns\"\n"
n, err := fmt.Sscanf(contents(errorLog), format, &line)
if n != 1 || err != nil {
t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(errorLog))
}
want := fmt.Sprintf(format, line)
if contents(errorLog) != want {
t.Errorf("ErrorS has wrong format: \n got:\t%s\nwant:\t%s", contents(errorLog), want)
}
}

// Test that kvListFormat works as advertised.
func TestKvListFormat(t *testing.T) {
var testKVList = []struct {
keysValues []interface{}
want string
}{
{
keysValues: []interface{}{"pod", "kubedns"},
want: " pod=\"kubedns\"",
},
{
keysValues: []interface{}{"pod", "kubedns", "update", true},
want: " pod=\"kubedns\" update=true",
},
{
keysValues: []interface{}{"pod", "kubedns", "spec", struct {
X int
Y string
}{X: 76, Y: "strval"}},
want: " pod=\"kubedns\" spec=struct { X int; Y string }{X:76, Y:\"strval\"}",
},
{
keysValues: []interface{}{"pod", "kubedns", "values", []int{8, 6, 7, 5, 3, 0, 9}},
want: " pod=\"kubedns\" values=[]int{8, 6, 7, 5, 3, 0, 9}",
},
{
keysValues: []interface{}{"pod", "kubedns", "values", []string{"deployment", "svc", "configmap"}},
want: " pod=\"kubedns\" values=[]string{\"deployment\", \"svc\", \"configmap\"}",
},
{
keysValues: []interface{}{"pod", "kubedns", "maps", map[string]int{"three": 4}},
want: " pod=\"kubedns\" maps=map[string]int{\"three\":4}",
},
{
keysValues: []interface{}{"pod", KRef("kube-system", "kubedns"), "status", "ready"},
want: " pod=\"kube-system/kubedns\" status=\"ready\"",
},
{
keysValues: []interface{}{"pod", KRef("", "kubedns"), "status", "ready"},
want: " pod=\"kubedns\" status=\"ready\"",
},
{
keysValues: []interface{}{"pod", KObj(mockKmeta{"test-name", "test-ns"}), "status", "ready"},
want: " pod=\"test-ns/test-name\" status=\"ready\"",
},
{
keysValues: []interface{}{"pod", KObj(mockKmeta{"test-name", ""}), "status", "ready"},
want: " pod=\"test-name\" status=\"ready\"",
},
}

for _, d := range testKVList {
b := &bytes.Buffer{}
kvListFormat(b, d.keysValues...)
if b.String() != d.want {
t.Errorf("kvlist format error:\n got:\n\t%s\nwant:\t%s", b.String(), d.want)
}
}
}

0 comments on commit bf9f969

Please sign in to comment.