From a020d51bcc18cdf789a2aff80255865fe37d1e80 Mon Sep 17 00:00:00 2001 From: Jan Hoon Date: Tue, 29 Oct 2024 22:31:05 +0000 Subject: [PATCH] feat: allow external loggers to be used with glog as default Signed-off-by: Jan Hoon --- ginoauth2.go | 21 ------------- logging.go | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ logging_test.go | 45 +++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 logging.go create mode 100644 logging_test.go diff --git a/ginoauth2.go b/ginoauth2.go index 38af0ee..65a7deb 100644 --- a/ginoauth2.go +++ b/ginoauth2.go @@ -64,7 +64,6 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/golang/glog" "golang.org/x/oauth2" ) @@ -102,26 +101,6 @@ func maskAccessToken(a interface{}) string { return s } -func logf(l func(string, ...interface{}), f string, args ...interface{}) { - for i := range args { - args[i] = maskAccessToken(args[i]) - } - - l(f, args...) -} - -func errorf(f string, args ...interface{}) { - logf(glog.Errorf, f, args...) -} - -func infof(f string, args ...interface{}) { - logf(glog.Infof, f, args...) -} - -func infofv2(f string, args ...interface{}) { - logf(glog.V(2).Infof, f, args...) -} - func extractToken(r *http.Request) (*oauth2.Token, error) { hdr := r.Header.Get("Authorization") if hdr == "" { diff --git a/logging.go b/logging.go new file mode 100644 index 0000000..fa9c369 --- /dev/null +++ b/logging.go @@ -0,0 +1,82 @@ +package ginoauth2 + +import ( + "fmt" + "io" + "os" + + "github.com/golang/glog" +) + +// Logger is the interface used by GinOAuth2 to log messages. +type Logger interface { + Errorf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Debugf(format string, args ...interface{}) +} + +type glogLogger struct { + output io.Writer +} + +// DefaultLogger is the default logger used by GinOAuth2 if no other logger is provided. +// To use a different logger, set the DefaultLogger variable to a logger of your choice. +// Replacement loggers must implement the Logger interface. +// +// Example: +// +// import "github.com/zalando/gin-oauth2" +// +// ginoauth2.DefaultLogger = &logrusLogger{} // use logrus +var DefaultLogger Logger = &glogLogger{output: os.Stderr} + +func maskLogArgs(args ...interface{}) []interface{} { + for i := range args { + args[i] = maskAccessToken(args[i]) + } + + return args +} + +// SetOutput sets the output destination for the logger +func (gl *glogLogger) setOutput(w io.Writer) { + gl.output = w +} + +// Errorf is a logging function using glog.Errorf +func (gl *glogLogger) Errorf(f string, args ...interface{}) { + glog.ErrorDepth(1, fmt.Sprintf(f, args...)) + if gl.output != nil { + fmt.Fprintf(gl.output, f+"\n", args...) + } +} + +// Infof is a logging function using glog.Infof +func (gl *glogLogger) Infof(f string, args ...interface{}) { + glog.InfoDepth(1, fmt.Sprintf(f, args...)) + if gl.output != nil { + fmt.Fprintf(gl.output, f+"\n", args...) + } +} + +// Debugf is a verbose logging function using glog.V(2) +func (gl *glogLogger) Debugf(f string, args ...interface{}) { + if glog.V(2) { + glog.InfoDepth(1, fmt.Sprintf(f, args...)) + } + if gl.output != nil { + fmt.Fprintf(gl.output, f+"\n", args...) + } +} + +func errorf(f string, args ...interface{}) { + DefaultLogger.Errorf(f, maskLogArgs(args...)...) +} + +func infof(f string, args ...interface{}) { + DefaultLogger.Infof(f, maskLogArgs(args...)...) +} + +func infofv2(f string, args ...interface{}) { + DefaultLogger.Debugf(f, maskLogArgs(args...)...) +} diff --git a/logging_test.go b/logging_test.go new file mode 100644 index 0000000..501c37f --- /dev/null +++ b/logging_test.go @@ -0,0 +1,45 @@ +package ginoauth2 + +import ( + "bytes" + "fmt" + "strings" + "testing" +) + +type mockLogger struct{ buffer bytes.Buffer } + +func (m *mockLogger) Errorf(format string, args ...interface{}) { + m.buffer.WriteString(fmt.Sprintf("ERROR: "+format, args...)) +} +func (m *mockLogger) Infof(format string, args ...interface{}) { + m.buffer.WriteString(fmt.Sprintf("INFO: "+format, args...)) +} +func (m *mockLogger) Debugf(format string, args ...interface{}) { + m.buffer.WriteString(fmt.Sprintf("DEBUG: "+format, args...)) +} + +func TestLogWithMaskedAccessToken(t *testing.T) { + mockLog := &mockLogger{} + DefaultLogger = mockLog + tests := []struct{ name, input, expected string }{ + {"With access token", "&access_token=abcdefghijklmnop&", "INFO: &"}, + {"Without access token", "no_token_here", "INFO: no_token_here"}, + {"Empty string", "", "INFO: "}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockLog.buffer.Reset() + + infof("%s", tt.input) + + logOutput := mockLog.buffer.String() + if logOutput != tt.expected { + t.Errorf("Expected log to contain %q, got %q", tt.expected, logOutput) + } + if strings.Contains(logOutput, "abcdefghijklmnop") { + t.Errorf("Log should not contain the original token") + } + }) + } +}