Skip to content

Commit a72e5e8

Browse files
committed
Add contextual loggin based recorder
Signed-off-by: Jian Qiu <[email protected]>
1 parent eb2d8ba commit a72e5e8

File tree

22 files changed

+2416
-31
lines changed

22 files changed

+2416
-31
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package events
2+
3+
import (
4+
"context"
5+
6+
"k8s.io/apimachinery/pkg/runtime"
7+
eventsv1 "k8s.io/client-go/kubernetes/typed/events/v1"
8+
kevents "k8s.io/client-go/tools/events"
9+
)
10+
11+
// NewEventRecorder creates a new event recorder for the given controller, it will also log the events
12+
func NewEventRecorder(ctx context.Context, scheme *runtime.Scheme,
13+
eventsClient eventsv1.EventsV1Interface, controllerName string) (kevents.EventRecorder, error) {
14+
broadcaster := kevents.NewBroadcaster(&kevents.EventSinkImpl{Interface: eventsClient})
15+
err := broadcaster.StartRecordingToSinkWithContext(ctx)
16+
if err != nil {
17+
return nil, err
18+
}
19+
broadcaster.StartStructuredLogging(0)
20+
recorder := broadcaster.NewRecorder(scheme, controllerName)
21+
return recorder, nil
22+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package events
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"k8s.io/klog/v2"
8+
)
9+
10+
// ContextualLoggingEventRecorder implements a recorder with contextual logging
11+
type ContextualLoggingEventRecorder struct {
12+
component string
13+
}
14+
15+
// NewContextualLoggingEventRecorder provides event recorder that will log all recorded events via klog.
16+
func NewContextualLoggingEventRecorder(component string) Recorder {
17+
return &ContextualLoggingEventRecorder{
18+
component: component,
19+
}
20+
}
21+
22+
func (r *ContextualLoggingEventRecorder) ComponentName() string {
23+
return r.component
24+
}
25+
26+
func (r *ContextualLoggingEventRecorder) ForComponent(component string) Recorder {
27+
newRecorder := *r
28+
newRecorder.component = component
29+
return &newRecorder
30+
}
31+
32+
func (r *ContextualLoggingEventRecorder) Shutdown() {}
33+
34+
func (r *ContextualLoggingEventRecorder) WithComponentSuffix(suffix string) Recorder {
35+
return r.ForComponent(fmt.Sprintf("%s-%s", r.ComponentName(), suffix))
36+
}
37+
38+
func (r *ContextualLoggingEventRecorder) Event(ctx context.Context, reason, message string) {
39+
logger := klog.FromContext(ctx)
40+
logger.Info(fmt.Sprintf("INFO: %s", message), "component", r.component, "reason", reason)
41+
}
42+
43+
func (r *ContextualLoggingEventRecorder) Eventf(ctx context.Context, reason, messageFmt string, args ...interface{}) {
44+
r.Event(ctx, reason, fmt.Sprintf(messageFmt, args...))
45+
}
46+
47+
func (r *ContextualLoggingEventRecorder) Warning(ctx context.Context, reason, message string) {
48+
logger := klog.FromContext(ctx)
49+
logger.Info(fmt.Sprintf("WARNING: %s", message), "component", r.component, "reason", reason)
50+
}
51+
52+
func (r *ContextualLoggingEventRecorder) Warningf(ctx context.Context, reason, messageFmt string, args ...interface{}) {
53+
r.Warning(ctx, reason, fmt.Sprintf(messageFmt, args...))
54+
}

pkg/basecontroller/events/recorder.go

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import (
1313

1414
// Recorder is a simple event recording interface.
1515
type Recorder interface {
16-
Event(reason, message string)
17-
Eventf(reason, messageFmt string, args ...interface{})
18-
Warning(reason, message string)
19-
Warningf(reason, messageFmt string, args ...interface{})
16+
Event(ctx context.Context, reason, message string)
17+
Eventf(ctx context.Context, reason, messageFmt string, args ...interface{})
18+
Warning(ctx context.Context, reason, message string)
19+
Warningf(ctx context.Context, reason, messageFmt string, args ...interface{})
2020

2121
// ForComponent allows to fiddle the component name before sending the event to sink.
2222
// Making more unique components will prevent the spam filter in upstream event sink from dropping
@@ -26,9 +26,6 @@ type Recorder interface {
2626
// WithComponentSuffix is similar to ForComponent except it just suffix the current component name instead of overriding.
2727
WithComponentSuffix(componentNameSuffix string) Recorder
2828

29-
// WithContext allows to set a context for event create API calls.
30-
WithContext(ctx context.Context) Recorder
31-
3229
// ComponentName returns the current source component name for the event.
3330
// This allows to suffix the original component name with 'sub-component'.
3431
ComponentName() string
@@ -50,9 +47,6 @@ type recorder struct {
5047
eventClient corev1client.EventInterface
5148
involvedObjectRef *corev1.ObjectReference
5249
sourceComponent string
53-
54-
// TODO: This is not the right way to pass the context, but there is no other way without breaking event interface
55-
ctx context.Context
5650
}
5751

5852
func (r *recorder) ComponentName() string {
@@ -67,44 +61,31 @@ func (r *recorder) ForComponent(componentName string) Recorder {
6761
return &newRecorderForComponent
6862
}
6963

70-
func (r *recorder) WithContext(ctx context.Context) Recorder {
71-
r.ctx = ctx
72-
return r
73-
}
74-
7564
func (r *recorder) WithComponentSuffix(suffix string) Recorder {
7665
return r.ForComponent(fmt.Sprintf("%s-%s", r.ComponentName(), suffix))
7766
}
7867

7968
// Eventf emits the normal type event and allow formatting of message.
80-
func (r *recorder) Eventf(reason, messageFmt string, args ...interface{}) {
81-
r.Event(reason, fmt.Sprintf(messageFmt, args...))
69+
func (r *recorder) Eventf(ctx context.Context, reason, messageFmt string, args ...interface{}) {
70+
r.Event(ctx, reason, fmt.Sprintf(messageFmt, args...))
8271
}
8372

8473
// Warningf emits the warning type event and allow formatting of message.
85-
func (r *recorder) Warningf(reason, messageFmt string, args ...interface{}) {
86-
r.Warning(reason, fmt.Sprintf(messageFmt, args...))
74+
func (r *recorder) Warningf(ctx context.Context, reason, messageFmt string, args ...interface{}) {
75+
r.Warning(ctx, reason, fmt.Sprintf(messageFmt, args...))
8776
}
8877

8978
// Event emits the normal type event.
90-
func (r *recorder) Event(reason, message string) {
79+
func (r *recorder) Event(ctx context.Context, reason, message string) {
9180
event := makeEvent(r.involvedObjectRef, r.sourceComponent, corev1.EventTypeNormal, reason, message)
92-
ctx := context.Background()
93-
if r.ctx != nil {
94-
ctx = r.ctx
95-
}
9681
if _, err := r.eventClient.Create(ctx, event, metav1.CreateOptions{}); err != nil {
9782
klog.Warningf("Error creating event %+v: %v", event, err)
9883
}
9984
}
10085

10186
// Warning emits the warning type event.
102-
func (r *recorder) Warning(reason, message string) {
87+
func (r *recorder) Warning(ctx context.Context, reason, message string) {
10388
event := makeEvent(r.involvedObjectRef, r.sourceComponent, corev1.EventTypeWarning, reason, message)
104-
ctx := context.Background()
105-
if r.ctx != nil {
106-
ctx = r.ctx
107-
}
10889
if _, err := r.eventClient.Create(ctx, event, metav1.CreateOptions{}); err != nil {
10990
klog.Warningf("Error creating event %+v: %v", event, err)
11091
}

pkg/basecontroller/factory/controller_context.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ package factory
22

33
import (
44
"fmt"
5-
65
"k8s.io/apimachinery/pkg/runtime"
76
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
87
"k8s.io/client-go/tools/cache"
98
"k8s.io/client-go/util/workqueue"
9+
"open-cluster-management.io/sdk-go/pkg/basecontroller/events"
1010
)
1111

1212
// syncContext implements SyncContext and provide user access to queue and object that caused
1313
// the sync to be triggered.
1414
type syncContext struct {
15-
queue workqueue.TypedRateLimitingInterface[string]
15+
queue workqueue.TypedRateLimitingInterface[string]
16+
recorder events.Recorder
1617
}
1718

1819
var _ SyncContext = syncContext{}
@@ -24,13 +25,18 @@ func NewSyncContext(name string) SyncContext {
2425
workqueue.DefaultTypedControllerRateLimiter[string](),
2526
workqueue.TypedRateLimitingQueueConfig[string]{Name: name},
2627
),
28+
recorder: events.NewContextualLoggingEventRecorder(name),
2729
}
2830
}
2931

3032
func (c syncContext) Queue() workqueue.TypedRateLimitingInterface[string] {
3133
return c.queue
3234
}
3335

36+
func (c syncContext) Recorder() events.Recorder {
37+
return c.recorder
38+
}
39+
3440
// eventHandler provides default event handler that is added to an informers passed to controller factory.
3541
func (c syncContext) eventHandler(queueKeysFunc ObjectQueueKeysFunc, filter EventFilterFunc) cache.ResourceEventHandler {
3642
resourceEventHandler := cache.ResourceEventHandlerFuncs{

pkg/basecontroller/factory/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package factory
22

33
import (
44
"context"
5+
"open-cluster-management.io/sdk-go/pkg/basecontroller/events"
56

67
"k8s.io/client-go/util/workqueue"
78
)
@@ -34,6 +35,9 @@ type SyncContext interface {
3435
// Queue gives access to controller queue. This can be used for manual requeue, although if a Sync() function return
3536
// an error, the object is automatically re-queued. Use with caution.
3637
Queue() workqueue.TypedRateLimitingInterface[string]
38+
39+
// Recorder returns a recorder to record events.
40+
Recorder() events.Recorder
3741
}
3842

3943
// SyncFunc is a function that contain main controller logic.

vendor/k8s.io/client-go/tools/events/OWNERS

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/k8s.io/client-go/tools/events/doc.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)