Merge pull request #135419 from pohly/apimachinery-handle-errors-helper

apimachinery: clarify ErrorHandler and provide helper for formatting
This commit is contained in:
Kubernetes Prow Robot
2026-03-12 05:13:42 +05:30
committed by GitHub
2 changed files with 68 additions and 0 deletions

View File

@@ -17,14 +17,17 @@ limitations under the License.
package runtime
import (
"bytes"
"context"
"fmt"
"net/http"
"runtime"
"strings"
"sync"
"time"
"k8s.io/klog/v2"
"k8s.io/klog/v2/textlogger"
)
var (
@@ -154,8 +157,37 @@ var ErrorHandlers = []ErrorHandler{
backoffError(1 * time.Millisecond),
}
// ErrorHandler is called indirectly through [HandleError], [HandleErrorWithContext] or [HandleErrorWithLogger].
// It is passed the same parameters that a structured logging backend needs to log a problem.
// It follows the semantic described for [HandleErrorWithContext] and [logr.Logger.Error]:
// - err is optional and may be nil
// - msg is string that describes the problem
// - keysAndValues contains additional information that varies between different occurrences of the problem
//
// [ErrorToString] can be used to convert these parameters into a single string, using the klog text output.
type ErrorHandler func(ctx context.Context, err error, msg string, keysAndValues ...interface{})
// ErrorToString takes the parameters passed to [ErrorHandler] and
// formats them as a string using the klog text output.
//
// If any of the values is a multi-line string, then the resulting
// string also uses line breaks and indention for the sake of readability.
// Does not include a trailing newline.
//
// Use errors.New if an error instead of a string is needed.
func ErrorToString(err error, msg string, keysAndValues ...interface{}) string {
var buffer bytes.Buffer
config := textlogger.NewConfig(
textlogger.Output(&buffer),
textlogger.WithHeader(false),
)
logger := textlogger.NewLogger(config)
logger.Error(err, msg, keysAndValues...)
result := buffer.String()
result = strings.TrimSpace(result)
return result
}
// HandlerError is a method to invoke when a non-user facing piece of code cannot
// return an error and needs to indicate it has been ignored. Invoking this method
// is preferable to logging the error - the default behavior is to log but the

View File

@@ -311,3 +311,39 @@ func TestHandleError(t *testing.T) {
})
}
}
func TestErrorToString(t *testing.T) {
for name, tt := range map[string]struct {
err error
msg string
kvs []any
expectString string
}{
"simple": {
errors.New("some error"),
"Unhandled error",
nil,
`"Unhandled error" err="some error"`,
},
"nil-error": {
nil,
"Some problem occurred",
nil,
`"Some problem occurred"`,
},
"keys-and-values": {
errors.New("some error"),
"Some error occurred",
[]any{"str", "foobar", "int", 1, "multiLine", "line 1\nline 2"},
`"Some error occurred" err="some error" str="foobar" int=1 multiLine=<
line 1
line 2
>`,
},
} {
t.Run(name, func(t *testing.T) {
actualString := ErrorToString(tt.err, tt.msg, tt.kvs...)
assert.Equal(t, tt.expectString, actualString)
})
}
}