-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add custom Linter to not allow improper usage of logger.Error(nil, ..…
….) calls and fix found linter issues. (#1599) Fix: Prevent nil errors in setupLog.Error to ensure proper logging Closes; #1566 Closes: #1556 Furthermore, it solves a similar scenario in the EventHandler code implementation found with the new custom linter check.
- Loading branch information
1 parent
c451127
commit 4304961
Showing
11 changed files
with
239 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package analyzers | ||
|
||
import ( | ||
"testing" | ||
|
||
"golang.org/x/tools/go/analysis/analysistest" | ||
) | ||
|
||
func TestSetupLogErrorCheck(t *testing.T) { | ||
testdata := analysistest.TestData() | ||
analysistest.Run(t, testdata, SetupLogErrorCheck) | ||
} |
119 changes: 119 additions & 0 deletions
119
hack/ci/custom-linters/analyzers/setuplognilerrorcheck.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package analyzers | ||
|
||
import ( | ||
"bytes" | ||
"go/ast" | ||
"go/format" | ||
"go/token" | ||
"go/types" | ||
|
||
"golang.org/x/tools/go/analysis" | ||
) | ||
|
||
var SetupLogErrorCheck = &analysis.Analyzer{ | ||
Name: "setuplogerrorcheck", | ||
Doc: "Detects improper usage of logger.Error() calls, ensuring the first argument is a non-nil error.", | ||
Run: runSetupLogErrorCheck, | ||
} | ||
|
||
func runSetupLogErrorCheck(pass *analysis.Pass) (interface{}, error) { | ||
for _, f := range pass.Files { | ||
ast.Inspect(f, func(n ast.Node) bool { | ||
callExpr, ok := n.(*ast.CallExpr) | ||
if !ok { | ||
return true | ||
} | ||
|
||
// Ensure function being called is logger.Error | ||
selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr) | ||
if !ok || selectorExpr.Sel.Name != "Error" { | ||
return true | ||
} | ||
|
||
// Ensure receiver (logger) is identified | ||
ident, ok := selectorExpr.X.(*ast.Ident) | ||
if !ok { | ||
return true | ||
} | ||
|
||
// Verify if the receiver is logr.Logger | ||
obj := pass.TypesInfo.ObjectOf(ident) | ||
if obj == nil { | ||
return true | ||
} | ||
|
||
named, ok := obj.Type().(*types.Named) | ||
if !ok || named.Obj().Pkg() == nil || named.Obj().Pkg().Path() != "github.com/go-logr/logr" || named.Obj().Name() != "Logger" { | ||
return true | ||
} | ||
|
||
if len(callExpr.Args) == 0 { | ||
return true | ||
} | ||
|
||
// Get the actual source code line where the issue occurs | ||
var srcBuffer bytes.Buffer | ||
if err := format.Node(&srcBuffer, pass.Fset, callExpr); err != nil { | ||
return true | ||
} | ||
sourceLine := srcBuffer.String() | ||
|
||
// Check if the first argument of the error log is nil | ||
firstArg, ok := callExpr.Args[0].(*ast.Ident) | ||
if ok && firstArg.Name == "nil" { | ||
suggestedError := "errors.New(\"kind error (i.e. configuration error)\")" | ||
suggestedMessage := "\"error message describing the failed operation\"" | ||
|
||
if len(callExpr.Args) > 1 { | ||
if msgArg, ok := callExpr.Args[1].(*ast.BasicLit); ok && msgArg.Kind == token.STRING { | ||
suggestedMessage = msgArg.Value | ||
} | ||
} | ||
|
||
pass.Reportf(callExpr.Pos(), | ||
"Incorrect usage of 'logger.Error(nil, ...)'. The first argument must be a non-nil 'error'. "+ | ||
"Passing 'nil' results in silent failures, making debugging harder.\n\n"+ | ||
"\U0001F41B **What is wrong?**\n %s\n\n"+ | ||
"\U0001F4A1 **How to solve? Return the error, i.e.:**\n logger.Error(%s, %s, \"key\", value)\n\n", | ||
sourceLine, suggestedError, suggestedMessage) | ||
return true | ||
} | ||
|
||
// Ensure at least two arguments exist (error + message) | ||
if len(callExpr.Args) < 2 { | ||
pass.Reportf(callExpr.Pos(), | ||
"Incorrect usage of 'logger.Error(error, ...)'. Expected at least an error and a message string.\n\n"+ | ||
"\U0001F41B **What is wrong?**\n %s\n\n"+ | ||
"\U0001F4A1 **How to solve?**\n Provide a message, e.g. logger.Error(err, \"descriptive message\")\n\n", | ||
sourceLine) | ||
return true | ||
} | ||
|
||
// Ensure key-value pairs (if any) are valid | ||
if (len(callExpr.Args)-2)%2 != 0 { | ||
pass.Reportf(callExpr.Pos(), | ||
"Incorrect usage of 'logger.Error(error, \"msg\", ...)'. Key-value pairs must be provided after the message, but an odd number of arguments was found.\n\n"+ | ||
"\U0001F41B **What is wrong?**\n %s\n\n"+ | ||
"\U0001F4A1 **How to solve?**\n Ensure all key-value pairs are complete, e.g. logger.Error(err, \"msg\", \"key\", value, \"key2\", value2)\n\n", | ||
sourceLine) | ||
return true | ||
} | ||
|
||
for i := 2; i < len(callExpr.Args); i += 2 { | ||
keyArg := callExpr.Args[i] | ||
keyType := pass.TypesInfo.TypeOf(keyArg) | ||
if keyType == nil || keyType.String() != "string" { | ||
pass.Reportf(callExpr.Pos(), | ||
"Incorrect usage of 'logger.Error(error, \"msg\", key, value)'. Keys in key-value pairs must be strings, but got: %s.\n\n"+ | ||
"\U0001F41B **What is wrong?**\n %s\n\n"+ | ||
"\U0001F4A1 **How to solve?**\n Ensure keys are strings, e.g. logger.Error(err, \"msg\", \"key\", value)\n\n", | ||
keyType, sourceLine) | ||
return true | ||
} | ||
} | ||
|
||
return true | ||
}) | ||
} | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module testdata | ||
|
||
go 1.23.4 | ||
|
||
require github.com/go-logr/logr v1.4.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= | ||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/go-logr/logr" | ||
) | ||
|
||
func testLogger() { | ||
var logger logr.Logger | ||
var err error | ||
var value int | ||
|
||
// Case 1: Nil error - Ensures the first argument cannot be nil. | ||
logger.Error(nil, "message") // want ".*results in silent failures, making debugging harder.*" | ||
|
||
// Case 2: Odd number of key-value arguments - Ensures key-value pairs are complete. | ||
logger.Error(err, "message", "key1") // want ".*Key-value pairs must be provided after the message, but an odd number of arguments was found.*" | ||
|
||
// Case 3: Key in key-value pair is not a string - Ensures keys in key-value pairs are strings. | ||
logger.Error(err, "message", 123, value) // want ".*Ensure keys are strings.*" | ||
|
||
// Case 4: Values are passed without corresponding keys - Ensures key-value arguments are structured properly. | ||
logger.Error(err, "message", value, "key2", value) // want ".*Key-value pairs must be provided after the message, but an odd number of arguments was found.*" | ||
|
||
// Case 5: Correct Usage - Should not trigger any warnings. | ||
logger.Error(err, "message", "key1", value, "key2", "value") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package main | ||
|
||
import ( | ||
"golang.org/x/tools/go/analysis" | ||
"golang.org/x/tools/go/analysis/unitchecker" | ||
|
||
"github.com/operator-framework/operator-controller/hack/ci/custom-linters/analyzers" | ||
) | ||
|
||
// Define the custom Linters implemented in the project | ||
var customLinters = []*analysis.Analyzer{ | ||
analyzers.SetupLogErrorCheck, | ||
} | ||
|
||
func main() { | ||
unitchecker.Main(customLinters...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters