aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
authorGravatar Shane Xie <xh4n3@users.noreply.github.com> 2022-07-18 21:52:06 +0800
committerGravatar GitHub <noreply@github.com> 2022-07-18 09:52:06 -0400
commitedbe02c0f91f0a9dae27164ab87115d59e76882d (patch)
tree103a2bf5879f5974d207807f9b651207b5ee5657 /plugin
parent29f3dcfa10b45cc5a38c472c4d48d08d530e644e (diff)
downloadcoredns-edbe02c0f91f0a9dae27164ab87115d59e76882d.tar.gz
coredns-edbe02c0f91f0a9dae27164ab87115d59e76882d.tar.zst
coredns-edbe02c0f91f0a9dae27164ab87115d59e76882d.zip
core: add log listeners for k8s_event plugin (#5451)
add log listener interface Signed-off-by: xh4n3 <xyn1016@gmail.com>
Diffstat (limited to 'plugin')
-rw-r--r--plugin/pkg/log/listener.go142
-rw-r--r--plugin/pkg/log/listener_test.go121
-rw-r--r--plugin/pkg/log/plugin.go44
3 files changed, 299 insertions, 8 deletions
diff --git a/plugin/pkg/log/listener.go b/plugin/pkg/log/listener.go
new file mode 100644
index 000000000..c1dda2cac
--- /dev/null
+++ b/plugin/pkg/log/listener.go
@@ -0,0 +1,142 @@
+package log
+
+import (
+ "sync"
+)
+
+// Listener listens for all log prints of plugin loggers aka loggers with plugin name.
+// When a plugin logger gets called, it should first call the same method in the Listener object.
+// A usage example is, the external plugin k8s_event will replicate log prints to Kubernetes events.
+type Listener interface {
+ Name() string
+ Debug(plugin string, v ...interface{})
+ Debugf(plugin string, format string, v ...interface{})
+ Info(plugin string, v ...interface{})
+ Infof(plugin string, format string, v ...interface{})
+ Warning(plugin string, v ...interface{})
+ Warningf(plugin string, format string, v ...interface{})
+ Error(plugin string, v ...interface{})
+ Errorf(plugin string, format string, v ...interface{})
+ Fatal(plugin string, v ...interface{})
+ Fatalf(plugin string, format string, v ...interface{})
+}
+
+type listeners struct {
+ listeners []Listener
+ sync.RWMutex
+}
+
+var ls *listeners
+
+func init() {
+ ls = &listeners{}
+ ls.listeners = make([]Listener, 0)
+}
+
+// RegisterListener register a listener object.
+func RegisterListener(new Listener) error {
+ ls.Lock()
+ defer ls.Unlock()
+ for k, l := range ls.listeners {
+ if l.Name() == new.Name() {
+ ls.listeners[k] = new
+ return nil
+ }
+ }
+ ls.listeners = append(ls.listeners, new)
+ return nil
+}
+
+// DeregisterListener deregister a listener object.
+func DeregisterListener(old Listener) error {
+ ls.Lock()
+ defer ls.Unlock()
+ for k, l := range ls.listeners {
+ if l.Name() == old.Name() {
+ ls.listeners = append(ls.listeners[:k], ls.listeners[k+1:]...)
+ return nil
+ }
+ }
+ return nil
+}
+
+func (ls *listeners) debug(plugin string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Debug(plugin, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) debugf(plugin string, format string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Debugf(plugin, format, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) info(plugin string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Info(plugin, v...)
+ }
+ ls.RUnlock()
+
+}
+
+func (ls *listeners) infof(plugin string, format string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Infof(plugin, format, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) warning(plugin string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Warning(plugin, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) warningf(plugin string, format string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Warningf(plugin, format, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) error(plugin string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Error(plugin, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) errorf(plugin string, format string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Errorf(plugin, format, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) fatal(plugin string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Fatal(plugin, v...)
+ }
+ ls.RUnlock()
+}
+
+func (ls *listeners) fatalf(plugin string, format string, v ...interface{}) {
+ ls.RLock()
+ for _, l := range ls.listeners {
+ l.Fatalf(plugin, format, v...)
+ }
+ ls.RUnlock()
+}
diff --git a/plugin/pkg/log/listener_test.go b/plugin/pkg/log/listener_test.go
new file mode 100644
index 000000000..f050dad69
--- /dev/null
+++ b/plugin/pkg/log/listener_test.go
@@ -0,0 +1,121 @@
+package log
+
+import (
+ "bytes"
+ golog "log"
+ "strings"
+ "testing"
+)
+
+func TestRegisterAndDeregisterListener(t *testing.T) {
+ for _, name := range []string{"listener1", "listener2", "listener1"} {
+ err := RegisterListener(NewMockListener(name))
+ if err != nil {
+ t.Errorf("RegsiterListener Error %s", err)
+ }
+ }
+ if len(ls.listeners) != 2 {
+ t.Errorf("Expected number of listeners to be %d, got %d", 2, len(ls.listeners))
+ }
+ for _, name := range []string{"listener1", "listener2"} {
+ err := DeregisterListener(NewMockListener(name))
+ if err != nil {
+ t.Errorf("DeregsiterListener Error %s", err)
+ }
+ }
+ if len(ls.listeners) != 0 {
+ t.Errorf("Expected number of listeners to be %d, got %d", 0, len(ls.listeners))
+ }
+}
+
+func TestSingleListenerMock(t *testing.T) {
+ listener1Name := "listener1"
+ listener1Output := info + listener1Name + " mocked info"
+ testListenersCalled(t, []string{listener1Name}, []string{listener1Output})
+}
+
+func TestMultipleListenerMock(t *testing.T) {
+ listener1Name := "listener1"
+ listener1Output := info + listener1Name + " mocked info"
+ listener2Name := "listener2"
+ listener2Output := info + listener2Name + " mocked info"
+ testListenersCalled(t, []string{listener1Name, listener2Name}, []string{listener1Output, listener2Output})
+}
+
+func testListenersCalled(t *testing.T, listenerNames []string, outputs []string) {
+ for _, name := range listenerNames {
+ err := RegisterListener(NewMockListener(name))
+ if err != nil {
+ t.Errorf("RegsiterListener Error %s", err)
+ }
+ }
+ var f bytes.Buffer
+ const ts = "test"
+ golog.SetOutput(&f)
+ lg := NewWithPlugin("testplugin")
+ lg.Info(ts)
+ for _, str := range outputs {
+ if x := f.String(); !strings.Contains(x, str) {
+ t.Errorf("Expected log to contain %s, got %s", str, x)
+ }
+ }
+ for _, name := range listenerNames {
+ err := DeregisterListener(NewMockListener(name))
+ if err != nil {
+ t.Errorf("DeregsiterListener Error %s", err)
+ }
+ }
+
+}
+
+type mockListener struct {
+ name string
+}
+
+func NewMockListener(name string) *mockListener {
+ return &mockListener{name: name}
+}
+
+func (l *mockListener) Name() string {
+ return l.name
+}
+
+func (l *mockListener) Debug(plugin string, v ...interface{}) {
+ log(debug, l.name+" mocked debug")
+}
+
+func (l *mockListener) Debugf(plugin string, format string, v ...interface{}) {
+ log(debug, l.name+" mocked debug")
+}
+
+func (l *mockListener) Info(plugin string, v ...interface{}) {
+ log(info, l.name+" mocked info")
+}
+
+func (l *mockListener) Infof(plugin string, format string, v ...interface{}) {
+ log(info, l.name+" mocked info")
+}
+
+func (l *mockListener) Warning(plugin string, v ...interface{}) {
+ log(warning, l.name+" mocked warning")
+}
+
+func (l *mockListener) Warningf(plugin string, format string, v ...interface{}) {
+ log(warning, l.name+" mocked warning")
+}
+
+func (l *mockListener) Error(plugin string, v ...interface{}) {
+ log(err, l.name+" mocked error")
+}
+
+func (l *mockListener) Errorf(plugin string, format string, v ...interface{}) {
+ log(err, l.name+" mocked error")
+}
+
+func (l *mockListener) Fatal(plugin string, v ...interface{}) {
+ log(fatal, l.name+" mocked fatal")
+}
+
+func (l *mockListener) Fatalf(plugin string, format string, v ...interface{}) {
+ log(fatal, l.name+" mocked fatal")
+}
diff --git a/plugin/pkg/log/plugin.go b/plugin/pkg/log/plugin.go
index 8dbffa07e..1be79f111 100644
--- a/plugin/pkg/log/plugin.go
+++ b/plugin/pkg/log/plugin.go
@@ -27,6 +27,7 @@ func (p P) Debug(v ...interface{}) {
if !D.Value() {
return
}
+ ls.debug(p.plugin, v...)
p.log(debug, v...)
}
@@ -35,29 +36,56 @@ func (p P) Debugf(format string, v ...interface{}) {
if !D.Value() {
return
}
+ ls.debugf(p.plugin, format, v...)
p.logf(debug, format, v...)
}
// Info logs as log.Info.
-func (p P) Info(v ...interface{}) { p.log(info, v...) }
+func (p P) Info(v ...interface{}) {
+ ls.info(p.plugin, v...)
+ p.log(info, v...)
+}
// Infof logs as log.Infof.
-func (p P) Infof(format string, v ...interface{}) { p.logf(info, format, v...) }
+func (p P) Infof(format string, v ...interface{}) {
+ ls.infof(p.plugin, format, v...)
+ p.logf(info, format, v...)
+}
// Warning logs as log.Warning.
-func (p P) Warning(v ...interface{}) { p.log(warning, v...) }
+func (p P) Warning(v ...interface{}) {
+ ls.warning(p.plugin, v...)
+ p.log(warning, v...)
+}
// Warningf logs as log.Warningf.
-func (p P) Warningf(format string, v ...interface{}) { p.logf(warning, format, v...) }
+func (p P) Warningf(format string, v ...interface{}) {
+ ls.warningf(p.plugin, format, v...)
+ p.logf(warning, format, v...)
+}
// Error logs as log.Error.
-func (p P) Error(v ...interface{}) { p.log(err, v...) }
+func (p P) Error(v ...interface{}) {
+ ls.error(p.plugin, v...)
+ p.log(err, v...)
+}
// Errorf logs as log.Errorf.
-func (p P) Errorf(format string, v ...interface{}) { p.logf(err, format, v...) }
+func (p P) Errorf(format string, v ...interface{}) {
+ ls.errorf(p.plugin, format, v...)
+ p.logf(err, format, v...)
+}
// Fatal logs as log.Fatal and calls os.Exit(1).
-func (p P) Fatal(v ...interface{}) { p.log(fatal, v...); os.Exit(1) }
+func (p P) Fatal(v ...interface{}) {
+ ls.fatal(p.plugin, v...)
+ p.log(fatal, v...)
+ os.Exit(1)
+}
// Fatalf logs as log.Fatalf and calls os.Exit(1).
-func (p P) Fatalf(format string, v ...interface{}) { p.logf(fatal, format, v...); os.Exit(1) }
+func (p P) Fatalf(format string, v ...interface{}) {
+ ls.fatalf(p.plugin, format, v...)
+ p.logf(fatal, format, v...)
+ os.Exit(1)
+}