Skip to content

Commit 2728fe1

Browse files
committedMar 22, 2021
check usage of format specifier in structured log func
Signed-off-by: Umanga Chapagain <chapagainumanga@gmail.com>
1 parent e158208 commit 2728fe1

File tree

2 files changed

+48
-4
lines changed

2 files changed

+48
-4
lines changed
 

‎hack/tools/logcheck/main.go

+45-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"go/ast"
2222
"go/token"
23+
"strings"
2324

2425
"golang.org/x/exp/utf8string"
2526
"golang.org/x/tools/go/analysis"
@@ -49,8 +50,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
4950
// We are intrested in function calls, as we want to detect klog.* calls
5051
// passing all function calls to checkForFunctionExpr
5152
if fexpr, ok := n.(*ast.CallExpr); ok {
52-
53-
checkForFunctionExpr(fexpr.Fun, fexpr.Args, pass)
53+
checkForFunctionExpr(fexpr, pass)
5454
}
5555

5656
return true
@@ -60,7 +60,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
6060
}
6161

6262
// checkForFunctionExpr checks for unstructured logging function, prints error if found any.
63-
func checkForFunctionExpr(fun ast.Expr, args []ast.Expr, pass *analysis.Pass) {
63+
func checkForFunctionExpr(fexpr *ast.CallExpr, pass *analysis.Pass) {
64+
65+
fun := fexpr.Fun
66+
args := fexpr.Args
6467

6568
/* we are extracting external package function calls e.g. klog.Infof fmt.Printf
6669
and eliminating calls like setLocalHost()
@@ -85,6 +88,11 @@ func checkForFunctionExpr(fun ast.Expr, args []ast.Expr, pass *analysis.Pass) {
8588
if ok && pName.Name == "klog" {
8689
// Matching if any unstructured logging function is used.
8790
if !isUnstructured((fName)) {
91+
// if format specifier is used, check for arg length will most probably fail
92+
// so check for format specifier first and skip if found
93+
if checkForFormatSpecifier(fexpr, pass) {
94+
return
95+
}
8896
if fName == "InfoS" {
8997
isKeysValid(args[1:], fun, pass, fName)
9098
} else if fName == "ErrorS" {
@@ -97,7 +105,6 @@ func checkForFunctionExpr(fun ast.Expr, args []ast.Expr, pass *analysis.Pass) {
97105
Message: msg,
98106
})
99107
}
100-
101108
}
102109
}
103110
}
@@ -159,3 +166,37 @@ func isKeysValid(keyValues []ast.Expr, fun ast.Expr, pass *analysis.Pass, funNam
159166
}
160167
}
161168
}
169+
170+
func checkForFormatSpecifier(expr *ast.CallExpr, pass *analysis.Pass) bool {
171+
if selExpr, ok := expr.Fun.(*ast.SelectorExpr); ok {
172+
// extracting function Name like Infof
173+
fName := selExpr.Sel.Name
174+
if specifier, found := hasFormatSpecifier(expr.Args); found {
175+
msg := fmt.Sprintf("structured logging function %q should not use format specifier %q", fName, specifier)
176+
pass.Report(analysis.Diagnostic{
177+
Pos: expr.Fun.Pos(),
178+
Message: msg,
179+
})
180+
return true
181+
}
182+
}
183+
return false
184+
}
185+
186+
func hasFormatSpecifier(fArgs []ast.Expr) (string, bool) {
187+
formatSpecifiers := []string{
188+
"%v", "%+v", "%#v", "%T",
189+
"%t", "%b", "%c", "%d", "%o", "%O", "%q", "%x", "%X", "%U",
190+
"%e", "%E", "%f", "%F", "%g", "%G", "%s", "%q", "%p",
191+
}
192+
for _, fArg := range fArgs {
193+
if arg, ok := fArg.(*ast.BasicLit); ok {
194+
for _, specifier := range formatSpecifiers {
195+
if strings.Contains(arg.Value, specifier) {
196+
return specifier, true
197+
}
198+
}
199+
}
200+
}
201+
return "", false
202+
}

‎hack/tools/logcheck/testdata/data.go

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func printUnstructuredLog() {
3939
klog.Fatalf("test log") // want `unstructured logging function "Fatalf" should not be used`
4040
klog.Fatalln("test log") // want `unstructured logging function "Fatalln" should not be used`
4141
klog.FatalDepth(1, "test log") // want `unstructured logging function "FatalDepth" should not be used`
42+
4243
}
4344

4445
func printStructuredLog() {
@@ -53,4 +54,6 @@ func printStructuredLog() {
5354
klog.InfoS("Starting container in a pod", map[string]string{"test1": "value"}, "containerID") // want `Key positional arguments are expected to be inlined constant strings. `
5455
testKey := "a"
5556
klog.ErrorS(nil, "Starting container in a pod", testKey, "containerID") // want `Key positional arguments are expected to be inlined constant strings. `
57+
klog.InfoS("test: %s", "testname") // want `structured logging function "InfoS" should not use format specifier "%s"`
58+
klog.ErrorS(nil, "test no.: %d", 1) // want `structured logging function "ErrorS" should not use format specifier "%d"`
5659
}

0 commit comments

Comments
 (0)
Please sign in to comment.