Skip to content

Commit 7ef2da0

Browse files
✨ Surface API Server warnings in client (#1468)
* Surface API Server warnings in client - Add a wrapper around `logr.Logger` that implements WarnignHandler - Add options in client.go for settiing up behaviour of warning handler Signed-off-by: Madhav Jivrajani <[email protected]> * rename logger type Signed-off-by: Madhav Jivrajani <[email protected]>
1 parent 2d6828d commit 7ef2da0

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

pkg/client/client.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,34 @@ import (
3131
"k8s.io/client-go/rest"
3232

3333
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
34+
"sigs.k8s.io/controller-runtime/pkg/log"
3435
)
3536

37+
// WarningHandlerOptions are options for configuring a
38+
// warning handler for the client which is responsible
39+
// for surfacing API Server warnings.
40+
type WarningHandlerOptions struct {
41+
// SuppressWarnings decides if the warnings from the
42+
// API server are suppressed or surfaced in the client.
43+
SuppressWarnings bool
44+
// AllowDuplicateLogs does not deduplicate the to-be
45+
// logged surfaced warnings messages. See
46+
// log.WarningHandlerOptions for considerations
47+
// regarding deuplication
48+
AllowDuplicateLogs bool
49+
}
50+
3651
// Options are creation options for a Client
3752
type Options struct {
3853
// Scheme, if provided, will be used to map go structs to GroupVersionKinds
3954
Scheme *runtime.Scheme
4055

4156
// Mapper, if provided, will be used to map GroupVersionKinds to Resources
4257
Mapper meta.RESTMapper
58+
59+
// Opts is used to configure the warning handler responsible for
60+
// surfacing and handling warnings messages sent by the API server.
61+
Opts WarningHandlerOptions
4362
}
4463

4564
// New returns a new Client using the provided config and Options.
@@ -61,6 +80,23 @@ func newClient(config *rest.Config, options Options) (*client, error) {
6180
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
6281
}
6382

83+
if !options.Opts.SuppressWarnings {
84+
// surface warnings
85+
logger := log.Log.WithName("KubeAPIWarningLogger")
86+
// Set a WarningHandler, the default WarningHandler
87+
// is log.KubeAPIWarningLogger with deduplication enabled.
88+
// See log.KubeAPIWarningLoggerOptions for considerations
89+
// regarding deduplication.
90+
rest.SetDefaultWarningHandler(
91+
log.NewKubeAPIWarningLogger(
92+
logger,
93+
log.KubeAPIWarningLoggerOptions{
94+
Deduplicate: !options.Opts.AllowDuplicateLogs,
95+
},
96+
),
97+
)
98+
}
99+
64100
// Init a scheme if none provided
65101
if options.Scheme == nil {
66102
options.Scheme = scheme.Scheme

pkg/log/warning_handler.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package log
18+
19+
import (
20+
"sync"
21+
22+
"github.com/go-logr/logr"
23+
)
24+
25+
// KubeAPIWarningLoggerOptions controls the behavior
26+
// of a rest.WarningHandler constructed using NewKubeAPIWarningLogger()
27+
type KubeAPIWarningLoggerOptions struct {
28+
// Deduplicate indicates a given warning message should only be written once.
29+
// Setting this to true in a long-running process handling many warnings can
30+
// result in increased memory use.
31+
Deduplicate bool
32+
}
33+
34+
// KubeAPIWarningLogger is a wrapper around
35+
// a provided logr.Logger that implements the
36+
// rest.WarningHandler interface.
37+
type KubeAPIWarningLogger struct {
38+
// logger is used to log responses with the warning header
39+
logger logr.Logger
40+
// opts contain options controlling warning output
41+
opts KubeAPIWarningLoggerOptions
42+
// writtenLock gurads written
43+
writtenLock sync.Mutex
44+
// used to keep track of already logged messages
45+
// and help in de-duplication.
46+
written map[string]struct{}
47+
}
48+
49+
// HandleWarningHeader handles logging for responses from API server that are
50+
// warnings with code being 299 and uses a logr.Logger for it's logging purposes.
51+
func (l *KubeAPIWarningLogger) HandleWarningHeader(code int, agent string, message string) {
52+
if code != 299 || len(message) == 0 {
53+
return
54+
}
55+
56+
if l.opts.Deduplicate {
57+
l.writtenLock.Lock()
58+
defer l.writtenLock.Unlock()
59+
60+
if _, alreadyLogged := l.written[message]; alreadyLogged {
61+
return
62+
}
63+
l.written[message] = struct{}{}
64+
}
65+
l.logger.Info(message)
66+
}
67+
68+
// NewKubeAPIWarningLogger returns an implementation of rest.WarningHandler that logs warnings
69+
// with code = 299 to the provided logr.Logger.
70+
func NewKubeAPIWarningLogger(l logr.Logger, opts KubeAPIWarningLoggerOptions) *KubeAPIWarningLogger {
71+
h := &KubeAPIWarningLogger{logger: l, opts: opts}
72+
if opts.Deduplicate {
73+
h.written = map[string]struct{}{}
74+
}
75+
return h
76+
}

0 commit comments

Comments
 (0)