client-go/test: warning handler with contextual logging

The default handler now uses contextual logging. Instead of

     warnings.go:106] warning 1

it now logs the caller of client-go and uses structured, contextual
logging

     main.go:100] "Warning" message="warning 1"

Users of client-go have the choice whether the handler that they provide uses
the traditional API (no API break!) or contextual logging.

Kubernetes-commit: 48fb886325fce4b16e4067caadb7bcd3044d460f
This commit is contained in:
Patrick Ohly
2024-09-02 17:51:01 +02:00
committed by Kubernetes Publisher
parent 9897373fe6
commit 2b2015d460
8 changed files with 343 additions and 61 deletions

View File

@@ -17,6 +17,7 @@ limitations under the License.
package rest
import (
"context"
"fmt"
"io"
"net/http"
@@ -33,8 +34,15 @@ type WarningHandler interface {
HandleWarningHeader(code int, agent string, text string)
}
// WarningHandlerWithContext is an interface for handling warning headers with
// support for contextual logging.
type WarningHandlerWithContext interface {
// HandleWarningHeaderWithContext is called with the warn code, agent, and text when a warning header is countered.
HandleWarningHeaderWithContext(ctx context.Context, code int, agent string, text string)
}
var (
defaultWarningHandler WarningHandler = WarningLogger{}
defaultWarningHandler WarningHandlerWithContext = WarningLogger{}
defaultWarningHandlerLock sync.RWMutex
)
@@ -43,33 +51,68 @@ var (
// - NoWarnings suppresses warnings.
// - WarningLogger logs warnings.
// - NewWarningWriter() outputs warnings to the provided writer.
//
// logcheck:context // SetDefaultWarningHandlerWithContext should be used instead of SetDefaultWarningHandler in code which supports contextual logging.
func SetDefaultWarningHandler(l WarningHandler) {
if l == nil {
SetDefaultWarningHandlerWithContext(nil)
return
}
SetDefaultWarningHandlerWithContext(warningLoggerNopContext{l: l})
}
// SetDefaultWarningHandlerWithContext is a variant of [SetDefaultWarningHandler] which supports contextual logging.
func SetDefaultWarningHandlerWithContext(l WarningHandlerWithContext) {
defaultWarningHandlerLock.Lock()
defer defaultWarningHandlerLock.Unlock()
defaultWarningHandler = l
}
func getDefaultWarningHandler() WarningHandler {
func getDefaultWarningHandler() WarningHandlerWithContext {
defaultWarningHandlerLock.RLock()
defer defaultWarningHandlerLock.RUnlock()
l := defaultWarningHandler
return l
}
// NoWarnings is an implementation of WarningHandler that suppresses warnings.
type warningLoggerNopContext struct {
l WarningHandler
}
func (w warningLoggerNopContext) HandleWarningHeaderWithContext(_ context.Context, code int, agent string, message string) {
w.l.HandleWarningHeader(code, agent, message)
}
// NoWarnings is an implementation of [WarningHandler] and [WarningHandlerWithContext] that suppresses warnings.
type NoWarnings struct{}
func (NoWarnings) HandleWarningHeader(code int, agent string, message string) {}
func (NoWarnings) HandleWarningHeaderWithContext(ctx context.Context, code int, agent string, message string) {
}
// WarningLogger is an implementation of WarningHandler that logs code 299 warnings
var _ WarningHandler = NoWarnings{}
var _ WarningHandlerWithContext = NoWarnings{}
// WarningLogger is an implementation of [WarningHandler] and [WarningHandlerWithContext] that logs code 299 warnings
type WarningLogger struct{}
func (WarningLogger) HandleWarningHeader(code int, agent string, message string) {
if code != 299 || len(message) == 0 {
return
}
klog.Warning(message)
klog.Background().Info("Warning: " + message)
}
func (WarningLogger) HandleWarningHeaderWithContext(ctx context.Context, code int, agent string, message string) {
if code != 299 || len(message) == 0 {
return
}
klog.FromContext(ctx).Info("Warning: " + message)
}
var _ WarningHandler = WarningLogger{}
var _ WarningHandlerWithContext = WarningLogger{}
type warningWriter struct {
// out is the writer to output warnings to
out io.Writer
@@ -134,14 +177,14 @@ func (w *warningWriter) WarningCount() int {
return w.writtenCount
}
func handleWarnings(headers http.Header, handler WarningHandler) []net.WarningHeader {
func handleWarnings(ctx context.Context, headers http.Header, handler WarningHandlerWithContext) []net.WarningHeader {
if handler == nil {
handler = getDefaultWarningHandler()
}
warnings, _ := net.ParseWarningHeaders(headers["Warning"])
for _, warning := range warnings {
handler.HandleWarningHeader(warning.Code, warning.Agent, warning.Text)
handler.HandleWarningHeaderWithContext(ctx, warning.Code, warning.Agent, warning.Text)
}
return warnings
}