diff --git a/logging.go b/logging.go index 3d9aad5..1042b97 100644 --- a/logging.go +++ b/logging.go @@ -18,15 +18,23 @@ package riak import ( "fmt" - "log" "os" ) +// Logger represents a leveled logger, with methods representing +// debug, warn, and error levels +type Logger interface { + Debug(v ...interface{}) + Debugf(format string, v ...interface{}) + Warn(v ...interface{}) + Warnf(format string, v ...interface{}) + Error(v ...interface{}) + Errorf(format string, v ...interface{}) +} + // If true, debug messages will be written to the log var EnableDebugLogging = false - -var errLogger = log.New(os.Stderr, "", log.LstdFlags) -var stdLogger = log.New(os.Stderr, "", log.LstdFlags) +var logger Logger func init() { if debugEnvVar := os.Getenv("RIAK_GO_CLIENT_DEBUG"); debugEnvVar != "" { @@ -36,50 +44,45 @@ func init() { // SetLogger sets the standard logger used for // WARN and DEBUG (if enabled) -func SetLogger(logger *log.Logger) { - stdLogger = logger -} - -// SetErrorLogger sets the logger used for errors -func SetErrorLogger(logger *log.Logger) { - errLogger = logger +func SetLogger(l Logger) { + logger = l } // logDebug writes formatted string debug messages using Printf only if debug logging is enabled func logDebug(source, format string, v ...interface{}) { if EnableDebugLogging { - stdLogger.Printf(fmt.Sprintf("[DEBUG] %s %s", source, format), v...) + logger.Debugf(fmt.Sprintf("%s %s", source, format), v...) } } // logDebugln writes string debug messages using Println func logDebugln(source string, v ...interface{}) { if EnableDebugLogging { - stdLogger.Println("[DEBUG]", source, v) + logger.Debug(source, v) } } // logWarn writes formatted string warning messages using Printf func logWarn(source, format string, v ...interface{}) { - stdLogger.Printf(fmt.Sprintf("[WARNING] %s %s", source, format), v...) + logger.Warnf(fmt.Sprintf("%s %s", source, format), v...) } // logWarnln writes string warning messages using Println func logWarnln(source string, v ...interface{}) { - stdLogger.Println("[WARNING]", source, v) + logger.Warn(source, v) } // logError writes formatted string error messages using Printf func logError(source, format string, v ...interface{}) { - errLogger.Printf(fmt.Sprintf("[ERROR] %s %s", source, format), v...) + logger.Errorf(fmt.Sprintf("%s %s", source, format), v...) } // logErr writes err.Error() using Println func logErr(source string, err error) { - errLogger.Println("[ERROR]", source, err) + logger.Error(source, err) } // logErrorln writes an error message using Println func logErrorln(source string, v ...interface{}) { - errLogger.Println("[ERROR]", source, v) + logger.Error(source, v) } diff --git a/logging_test.go b/logging_test.go index c9b17b8..e873c0d 100644 --- a/logging_test.go +++ b/logging_test.go @@ -3,16 +3,75 @@ package riak import ( "bytes" "fmt" + "io" "log" "strings" "testing" ) +type fakeFancyLogger struct { + buf io.Writer + lastCalled string +} + +// appendNewline adds a newline, if one does not already exist at the +// end of the format string. This mimics log.Print[f|ln]'s behavior. +func appendNewline(s string, w io.Writer) { + // taken from stdlib's log.Printf + if len(s) == 0 || s[len(s)-1] != '\n' { + w.Write([]byte(string('\n'))) + } +} + +func (logger *fakeFancyLogger) Println(v ...interface{}) { + fmt.Fprintln(logger.buf, v...) + logger.lastCalled = "Println" +} + +func (logger *fakeFancyLogger) Printf(format string, v ...interface{}) { + fmt.Fprintf(logger.buf, format, v...) + appendNewline(format, logger.buf) + logger.lastCalled = "Printf" +} + +func (logger *fakeFancyLogger) Debug(v ...interface{}) { + fmt.Fprintln(logger.buf, v...) + logger.lastCalled = "Debug" +} + +func (logger *fakeFancyLogger) Debugf(format string, v ...interface{}) { + fmt.Fprintf(logger.buf, format, v...) + appendNewline(format, logger.buf) + logger.lastCalled = "Debugf" +} + +func (logger *fakeFancyLogger) Warn(v ...interface{}) { + fmt.Fprintln(logger.buf, v...) + logger.lastCalled = "Warn" +} + +func (logger *fakeFancyLogger) Warnf(format string, v ...interface{}) { + fmt.Fprintf(logger.buf, format, v...) + appendNewline(format, logger.buf) + logger.lastCalled = "Warnf" +} + +func (logger *fakeFancyLogger) Error(v ...interface{}) { + fmt.Fprintln(logger.buf, v...) + logger.lastCalled = "Error" +} + +func (logger *fakeFancyLogger) Errorf(format string, v ...interface{}) { + fmt.Fprintf(logger.buf, format, v...) + appendNewline(format, logger.buf) + logger.lastCalled = "Errorf" +} + func TestLog(t *testing.T) { EnableDebugLogging = true tests := []struct { - setLoggerFunc func(*log.Logger) + setLoggerFunc func(Logger) logFunc func(string, string, ...interface{}) prefix string }{ @@ -48,11 +107,54 @@ func TestLog(t *testing.T) { } } +func TestFancyLog(t *testing.T) { + EnableDebugLogging = true + + tests := []struct { + setLoggerFunc func(Logger) + logFunc func(string, string, ...interface{}) + shouldCall string + }{ + { + SetErrorLogger, + logError, + "Errorf", + }, + { + SetLogger, + logWarn, + "Warnf", + }, + { + SetLogger, + logDebug, + "Debugf", + }, + } + + for _, tt := range tests { + buf := &bytes.Buffer{} + fancyLogger := &fakeFancyLogger{buf: buf} + tt.setLoggerFunc(fancyLogger) + tt.logFunc("[test]", "Hello %s!", "World") + + actual := buf.String() + suffix := "[test] Hello World!\n" + + if !strings.HasSuffix(actual, suffix) { + t.Errorf("Expected %s to end with %s", actual, suffix) + } + if fancyLogger.lastCalled != tt.shouldCall { + t.Errorf("Expected call to %s, got %s", tt.shouldCall, fancyLogger.lastCalled) + } + } +} + func TestLogln(t *testing.T) { EnableDebugLogging = true tests := []struct { - setLoggerFunc func(*log.Logger) + setLoggerFunc func(Logger) logFunc func(string, ...interface{}) prefix string }{ @@ -88,6 +190,49 @@ func TestLogln(t *testing.T) { } } +func TestFancyLogln(t *testing.T) { + EnableDebugLogging = true + + tests := []struct { + setLoggerFunc func(Logger) + logFunc func(string, ...interface{}) + shouldCall string + }{ + { + SetErrorLogger, + logErrorln, + "Error", + }, + { + SetLogger, + logWarnln, + "Warn", + }, + { + SetLogger, + logDebugln, + "Debug", + }, + } + + for _, tt := range tests { + buf := &bytes.Buffer{} + fancyLogger := &fakeFancyLogger{buf: buf} + tt.setLoggerFunc(fancyLogger) + tt.logFunc("[test]", "Hello", "World!") + + actual := buf.String() + suffix := "[test] [Hello World!]\n" + + if !strings.HasSuffix(actual, suffix) { + t.Errorf("Expected %s to end with %s", actual, suffix) + } + if fancyLogger.lastCalled != tt.shouldCall { + t.Errorf("Expected call to %s, got %s", tt.shouldCall, fancyLogger.lastCalled) + } + } +} + func TestDebugDisabled(t *testing.T) { EnableDebugLogging = false