diff --git a/internal/log/log.go b/internal/log/log.go index d191e6b..64a9962 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -13,7 +13,7 @@ import ( "sync" ) -// Base supports logging with various log levels. +// Base supports logging at various log levels. type Base interface { // Debug logs a message at Debug level. Debug(args ...interface{}) @@ -87,7 +87,7 @@ func NewLogger(base Base) *Logger { return &Logger{base: base, level: DebugLevel} } -// Logger logs message to io.Writer with various log levels. +// Logger logs message to io.Writer at various log levels. type Logger struct { base Base @@ -121,6 +121,9 @@ const ( FatalLevel ) +// String is part of the fmt.Stringer interface. +// +// Used for testing and debugging purposes. func (l Level) String() string { switch l { case DebugLevel: @@ -167,14 +170,14 @@ func (l *Logger) Warn(args ...interface{}) { } func (l *Logger) Error(args ...interface{}) { - if !l.canLogAt(WarnLevel) { + if !l.canLogAt(ErrorLevel) { return } l.base.Error(args...) } func (l *Logger) Fatal(args ...interface{}) { - if !l.canLogAt(WarnLevel) { + if !l.canLogAt(FatalLevel) { return } l.base.Fatal(args...) diff --git a/internal/log/log_test.go b/internal/log/log_test.go index 925cfce..ac9fdf1 100644 --- a/internal/log/log_test.go +++ b/internal/log/log_test.go @@ -284,7 +284,9 @@ func TestLoggerErrorf(t *testing.T) { } } -func TestLoggerWithMinLevels(t *testing.T) { +func TestLoggerWithLowerLevels(t *testing.T) { + // Logger should not log messages at a level + // lower than the specified level. tests := []struct { level Level op string @@ -334,3 +336,53 @@ func TestLoggerWithMinLevels(t *testing.T) { } } } + +func TestLoggerWithSameOrHigherLevels(t *testing.T) { + // Logger should log messages at a level + // same as or higher than the specified level. + tests := []struct { + level Level + op string + }{ + // same level + {DebugLevel, "Debug"}, + {InfoLevel, "Infof"}, + {WarnLevel, "Warn"}, + {ErrorLevel, "Errorf"}, + // higher level + {DebugLevel, "Info"}, + {InfoLevel, "Warnf"}, + {WarnLevel, "Error"}, + } + + for _, tc := range tests { + var buf bytes.Buffer + logger := NewLogger(newBase(&buf)) + logger.SetLevel(tc.level) + + switch tc.op { + case "Debug": + logger.Debug("hello") + case "Debugf": + logger.Debugf("hello, %s", "world") + case "Info": + logger.Info("hello") + case "Infof": + logger.Infof("hello, %s", "world") + case "Warn": + logger.Warn("hello") + case "Warnf": + logger.Warnf("hello, %s", "world") + case "Error": + logger.Error("hello") + case "Errorf": + logger.Errorf("hello, %s", "world") + default: + t.Fatalf("unexpected op: %q", tc.op) + } + + if buf.String() == "" { + t.Errorf("logger.%s did not output log message when level is set to %v", tc.op, tc.level) + } + } +} diff --git a/server.go b/server.go index 5580606..f8fc9f7 100644 --- a/server.go +++ b/server.go @@ -12,6 +12,7 @@ import ( "math/rand" "os" "runtime" + "strings" "sync" "time" @@ -138,7 +139,7 @@ func (fn ErrorHandlerFunc) HandleError(task *Task, err error, retried, maxRetry fn(task, err, retried, maxRetry) } -// Logger supports logging with various log levels. +// Logger supports logging at various log levels. type Logger interface { // Debug logs a message at Debug level. Debug(args ...interface{}) @@ -158,6 +159,8 @@ type Logger interface { } // LogLevel represents logging level. +// +// It satisfies flag.Value interface. type LogLevel int32 const ( @@ -181,6 +184,42 @@ const ( FatalLevel ) +// String is part of the flag.Value interface. +func (l *LogLevel) String() string { + switch *l { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warn" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + } + panic(fmt.Sprintf("asynq: unexpected log level: %v", *l)) +} + +// Set is part of the flag.Value interface. +func (l *LogLevel) Set(val string) error { + switch strings.ToLower(val) { + case "debug": + *l = DebugLevel + case "info": + *l = InfoLevel + case "warn", "warning": + *l = WarnLevel + case "error": + *l = ErrorLevel + case "fatal": + *l = FatalLevel + default: + return fmt.Errorf("asynq: unsupported log level %q", val) + } + return nil +} + // Formula taken from https://github.com/mperham/sidekiq. func defaultDelayFunc(n int, e error, t *Task) time.Duration { r := rand.New(rand.NewSource(time.Now().UnixNano())) diff --git a/server_test.go b/server_test.go index 31cccd9..6fb7f28 100644 --- a/server_test.go +++ b/server_test.go @@ -208,3 +208,32 @@ func TestServerWithFlakyBroker(t *testing.T) { srv.Stop() } + +func TestLogLevel(t *testing.T) { + tests := []struct { + flagVal string + want LogLevel + wantStr string + }{ + {"debug", DebugLevel, "debug"}, + {"Info", InfoLevel, "info"}, + {"WARN", WarnLevel, "warn"}, + {"warning", WarnLevel, "warn"}, + {"Error", ErrorLevel, "error"}, + {"fatal", FatalLevel, "fatal"}, + } + + for _, tc := range tests { + level := new(LogLevel) + if err := level.Set(tc.flagVal); err != nil { + t.Fatal(err) + } + if *level != tc.want { + t.Errorf("Set(%q): got %v, want %v", tc.flagVal, level, &tc.want) + continue + } + if got := level.String(); got != tc.wantStr { + t.Errorf("String() returned %q, want %q", got, tc.wantStr) + } + } +}