diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5f2bd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea* +.idea/* \ No newline at end of file diff --git a/README.md b/README.md index 02cbd79..6eb9e79 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,53 @@ -# logger +# Kris Nóva Logger -Ported from it's [original location](https://github.com/kubicorn/kubicorn/tree/master/pkg/logger) in the Kubicorn code base. +## History + +- [2017] Originally ported from it's [original location](https://github.com/kubicorn/kubicorn/tree/master/pkg/logger) in the Kubicorn code base. +- [2021] Refactored to support custom `io.Writer`'s
-# Rainbow logs + +## Install + +```bash +go get github.com/kris-nova/logger +``` + +## Basic Usage + +```go +package main + +import ( + "github.com/kris-nova/logger" + "os" +) + +func main() { + // Options + logger.Writer = os.Stdout // This is not needed + logger.BitwiseLevel = logger.LogCritical | logger.LogWarning // Customize your levels + logger.BitwiseLevel = logger.LogEverything // Turn everything on + logger.BitwiseLevel = logger.LogAlways // Only log Always() + logger.BitwiseLevel = logger.LogEverything // Turn everything back on + // + + // Log lines + logger.Debug("Check this out %d", 123) + logger.Info("Cool!") + logger.Success("Hooray!") + logger.Always("Hello!") + logger.Critical("Oh No!") + logger.Warning("Beware...") + logger.Deprecated("Don't do this!") + // +} + +``` + + +## Rainbow logs ```go @@ -12,74 +55,21 @@ package main import ( "github.com/kris-nova/logger" - "fmt" - "os" + lol "github.com/kris-nova/lolgopher" ) func main(){ + // + logger.Writer = lol.NewLolWriter() // Sometimes this will work better + logger.Writer = lol.NewTruecolorLolWriter() // Comment one of these out + // - logger.Fabulous = true - logger.Color = false - logger.Level = 4 - - err := fmt.Errorf("New error") - - logger.Always("This is always printed") - - logger.Success("Hooray a good thing happened!") - - logger.Info("we found an error: %v", err) - - logger.Debug("this is a useful message for software enigneers") - - logger.Warning("something bad happened but the software can still run") - - // Notice this does *NOT* exit! - logger.Critical("the software should stop running, this is bad") - - // Now we have to exit - os.Exit(123) + logger.BitwiseLevel = logger.LogEverything + logger.Always("Rainbow logging") + logger.Always("Rainbow logging") + logger.Always("Rainbow logging") + logger.Always("Rainbow logging") } -``` - -# Simple golang logger - -```go -package main - -import ( - "github.com/kris-nova/logger" - "fmt" - "os" -) - - -func main(){ - - // Most Verbose - //logger.Level = 4 - - // Normal - // No info or debug messages, only warnings and criticals - logger.Level = 2 - - // Off - //logger.Level = 0 - - err := fmt.Errorf("New error") - logger.Info("we found an error: %v", err) - - logger.Debug("this is a useful message for software enigneers") - - logger.Warning("something bad happened but the software can still run") - - // Notice this does *NOT* exit! - logger.Critical("the software should stop running, this is bad") - - // Now we have to exit - os.Exit(123) -} - -``` +``` \ No newline at end of file diff --git a/example/example.go b/example/example.go deleted file mode 100644 index ca27826..0000000 --- a/example/example.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "github.com/kris-nova/logger" - "fmt" - "os" -) - - -func main(){ - - // Most Verbose - //logger.Level = 4 - - // Normal - // No info or debug messages, only warnings and criticals - logger.Level = 2 - - // Off - //logger.Level = 0 - - logger.Always("This is always printed") - - logger.Success("Hooray a good thing happened!") - - err := fmt.Errorf("New error") - logger.Info("we found an error: %v", err) - - logger.Debug("this is a useful message for software enigneers") - - logger.Warning("something bad happened but the software can still run") - - // Notice this does *NOT* exit! - logger.Critical("the software should stop running, this is bad") - - // Now we have to exit - os.Exit(123) -} diff --git a/example/fabulous.go b/example/fabulous.go deleted file mode 100644 index 9c6a305..0000000 --- a/example/fabulous.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "github.com/kris-nova/logger" - "fmt" - "os" -) - - -func main(){ - - logger.Fabulous = true - logger.Color = false - logger.Level = 4 - - err := fmt.Errorf("New error") - - logger.Always("This is always printed") - - logger.Success("Hooray a good thing happened!") - - logger.Info("we found an error: %v", err) - - logger.Debug("this is a useful message for software enigneers") - - logger.Warning("something bad happened but the software can still run") - - // Notice this does *NOT* exit! - logger.Critical("the software should stop running, this is bad") - - // Now we have to exit - os.Exit(123) -} diff --git a/examples/legacy.go b/examples/legacy.go new file mode 100644 index 0000000..b0c504c --- /dev/null +++ b/examples/legacy.go @@ -0,0 +1,12 @@ +package main + +import "github.com/kris-nova/novaarchive/logger" + +func main() { + + logger.Level = 4 + logger.Always("Always logging") + logger.Info("Info logging") + logger.Debug("Debug logging") + +} diff --git a/examples/rainbow.go b/examples/rainbow.go new file mode 100644 index 0000000..7800e03 --- /dev/null +++ b/examples/rainbow.go @@ -0,0 +1,43 @@ +package main + +import ( + "github.com/kris-nova/logger" + lol "github.com/kris-nova/lolgopher" +) + +func main() { + // + logger.Writer = lol.NewLolWriter() // Sometimes this will work better + logger.Writer = lol.NewTruecolorLolWriter() // Comment one of these out + // + + logger.BitwiseLevel = logger.LogEverything + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Always(".....................") + logger.Debug("Debug logging") + logger.Critical("Stderr logging") +} diff --git a/examples/simple.go b/examples/simple.go new file mode 100644 index 0000000..cf96ab4 --- /dev/null +++ b/examples/simple.go @@ -0,0 +1,18 @@ +package main + +import ( + "os" + + "github.com/kris-nova/novaarchive/logger" +) + +func main() { + + logger.BitwiseLevel = logger.LogEverything + logger.Always("Always logging") + logger.Always("Always logging") + logger.Debug("Debug logging") + + logger.Writer = os.Stderr + logger.Critical("Stderr logging") +} diff --git a/go.mod b/go.mod deleted file mode 100644 index 831c64b..0000000 --- a/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/kris-nova/logger - -go 1.14 - -require ( - github.com/fatih/color v1.9.0 - github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b -) diff --git a/go.sum b/go.sum deleted file mode 100644 index fff877b..0000000 --- a/go.sum +++ /dev/null @@ -1,12 +0,0 @@ -github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b h1:xYEM2oBUhBEhQjrV+KJ9lEWDWYZoNVZUaBF++Wyljq4= -github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b/go.mod h1:V0HF/ZBlN86HqewcDC/cVxMmYDiRukWjSrgKLUAn9Js= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/logger.go b/logger.go index 5c21b5e..29b5c12 100644 --- a/logger.go +++ b/logger.go @@ -1,4 +1,4 @@ -// Copyright © 2017 +// Copyright © 2021 Kris Nóva // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,172 +20,167 @@ import ( "os" "strings" "time" - - "github.com/fatih/color" - lol "github.com/kris-nova/lolgopher" ) -type Logger func(format string, a ...interface{}) +type LoggerFunc func(format string, a ...interface{}) + +//type Logger interface { +// LineBytes(prefix, format string, a ...interface{}) []byte +// Line(prefix, format string, a ...interface{}) string +// Always(format string, a ...interface{}) +// Success(format string, a ...interface{}) +// Debug(format string, a ...interface{}) +// Info(format string, a ...interface{}) +// Warning(format string, a ...interface{}) +// Critical(format string, a ...interface{}) +//} const ( - AlwaysLabel = "✿" - CriticalLabel = "✖" - DebugLabel = "▶" - InfoLabel = "ℹ" - SuccessLabel = "✔" - WarningLabel = "!" + // [Log Constants] + // + // These are the bitwise values for the + // various log options. + // + // Also these are the string prefixes + // for the log lines. + LogAlways = 1 + PreAlways = "Always " + LogSuccess = 2 + PreSuccess = "Success " + LogCritical = 4 + PreCritical = "Critical " + LogWarning = 8 + PreWarning = "Warning " + LogInfo = 16 + PreInfo = "Info " + LogDebug = 32 + PreDebug = "Debug " + LogDeprecated = 64 + PreDeprecated = "Deprecated" + + LogLegacyLevel2 = LogAlways | LogSuccess | LogCritical | LogWarning | LogInfo + LogLegacyLevel2Deprecated = LogLegacyLevel2 | LogDeprecated + + // Enable all Logging levels + // [127] + LogEverything = LogAlways | LogSuccess | LogDebug | LogInfo | LogWarning | LogCritical | LogDeprecated ) +type WriterMode int + var ( - Level = 2 - Color = true - Fabulous = false - FabulousWriter = lol.NewLolWriter() - FabulousTrueWriter = lol.NewTruecolorLolWriter() - TestMode = false - Timestamps = true + + // BitwiseLevel is the preferred + // way of managing log levels. + // + // ----- [ Bitwise Chart ] ------ + // + // LogEverything (All Levels) + // LogAlways + // LogSuccess + // LogCritical + // LogWarning + // LogInfo + // LogDebug + // LogDeprecated + // + // TODO @kris-nova In the next release flip to LogEverything + // BitwiseLevel = LogEverything + BitwiseLevel = LogLegacyLevel2Deprecated + + // A custom io.Writer to use regardless of Mode + Writer io.Writer = os.Stdout + + // Layout is the time layout string to use + Layout string = time.RFC3339 ) -func Always(format string, a ...interface{}) { - a, w := extractLoggerArgs(format, a...) - s := fmt.Sprintf(label(format, AlwaysLabel), a...) - - if !TestMode { - if Color { - w = color.Output - s = color.GreenString(s) - } else if Fabulous { - w = FabulousWriter - } - } - - fmt.Fprint(w, s) +// LineBytes will format a log line, and return +// a slice of bytes []byte +func LineBytes(prefix, format string, a ...interface{}) []byte { + return []byte(Line(prefix, format, a...)) } -func Critical(format string, a ...interface{}) { - if Level >= 1 { - a, w := extractLoggerArgs(format, a...) - s := fmt.Sprintf(label(format, CriticalLabel), a...) - - if !TestMode { - if Color { - w = color.Output - s = color.RedString(s) - } else if Fabulous { - w = FabulousWriter - } - } - - fmt.Fprint(w, s) +// Line will format a log line, and return a string +func Line(prefix, format string, a ...interface{}) string { + if !strings.Contains(format, "\n") { + format = fmt.Sprintf("%s%s", format, "\n") } -} - -func Info(format string, a ...interface{}) { - if Level >= 3 { - a, w := extractLoggerArgs(format, a...) - s := fmt.Sprintf(label(format, InfoLabel), a...) - - if !TestMode { - if Color { - w = color.Output - s = color.CyanString(s) - } else if Fabulous { - w = FabulousWriter - } - } - - fmt.Fprint(w, s) - } -} - -func Success(format string, a ...interface{}) { - if Level >= 3 { - a, w := extractLoggerArgs(format, a...) - s := fmt.Sprintf(label(format, SuccessLabel), a...) - - if !TestMode { - if Color { - w = color.Output - s = color.CyanString(s) - } else if Fabulous { - w = FabulousWriter - } - } - - fmt.Fprint(w, s) - } -} - -func Debug(format string, a ...interface{}) { - if Level >= 4 { - a, w := extractLoggerArgs(format, a...) - s := fmt.Sprintf(label(format, DebugLabel), a...) - - if !TestMode { - if Color { - w = color.Output - s = color.GreenString(s) - } else if Fabulous { - w = FabulousWriter - } - } - - fmt.Fprint(w, s) - } -} - -func Warning(format string, a ...interface{}) { - if Level >= 2 { - a, w := extractLoggerArgs(format, a...) - s := fmt.Sprintf(label(format, WarningLabel), a...) - - if !TestMode { - if Color { - w = color.Output - s = color.GreenString(s) - } else if Fabulous { - w = FabulousWriter - } - } - - fmt.Fprint(w, s) - } -} - -func extractLoggerArgs(format string, a ...interface{}) ([]interface{}, io.Writer) { - var w io.Writer = os.Stdout - - if n := len(a); n > 0 { - // extract an io.Writer at the end of a - if value, ok := a[n-1].(io.Writer); ok { - w = value - a = a[0 : n-1] - } - } - - return a, w -} - -func label(format, label string) string { if Timestamps { - return labelWithTime(format, label) + now := time.Now() + fNow := now.Format(Layout) + prefix = fmt.Sprintf("%s [%s]", fNow, prefix) } else { - return labelWithoutTime(format, label) + prefix = fmt.Sprintf("[%s]", prefix) + } + return fmt.Sprintf("%s %s", prefix, fmt.Sprintf(format, a...)) +} + +// Always +func Always(format string, a ...interface{}) { + d() + a = legacyFindWriter(a...) + if BitwiseLevel&LogAlways != 0 { + fmt.Fprint(Writer, Line(PreAlways, format, a...)) } } -func labelWithTime(format, label string) string { - t := time.Now() - rfct := t.Format(time.RFC3339) - if !strings.Contains(format, "\n") { - format = fmt.Sprintf("%s%s", format, "\n") +// Success +func Success(format string, a ...interface{}) { + d() + a = legacyFindWriter(a...) + if BitwiseLevel&LogSuccess != 0 { + fmt.Fprint(Writer, Line(PreSuccess, format, a...)) } - return fmt.Sprintf("%s [%s] %s", rfct, label, format) } -func labelWithoutTime(format, label string) string { - if !strings.Contains(format, "\n") { - format = fmt.Sprintf("%s%s", format, "\n") +// Debug +func Debug(format string, a ...interface{}) { + d() + a = legacyFindWriter(a...) + if BitwiseLevel&LogDebug != 0 { + fmt.Fprint(Writer, Line(PreDebug, format, a...)) } - return fmt.Sprintf("[%s] %s", label, format) +} + +// Info +func Info(format string, a ...interface{}) { + d() + a = legacyFindWriter(a...) + if BitwiseLevel&LogInfo != 0 { + fmt.Fprint(Writer, Line(PreInfo, format, a...)) + } +} + +// Warning +func Warning(format string, a ...interface{}) { + d() + a = legacyFindWriter(a...) + if BitwiseLevel&LogWarning != 0 { + fmt.Fprint(Writer, Line(PreWarning, format, a...)) + } +} + +// Critical +func Critical(format string, a ...interface{}) { + d() + a = legacyFindWriter(a...) + if BitwiseLevel&LogCritical != 0 { + fmt.Fprint(Writer, Line(PreCritical, format, a...)) + } +} + +// Used to show deprecated log lines +func Deprecated(format string, a ...interface{}) { + a = legacyFindWriter(a...) + if BitwiseLevel&LogDeprecated != 0 { + fmt.Fprint(Writer, Line(PreDeprecated, format, a...)) + } +} + +// d is used by every function +// and is an easy way to add +// global logic/state to the logger +func d() { + checkDeprecatedValues() } diff --git a/logger_legacy.go b/logger_legacy.go new file mode 100644 index 0000000..ea3b711 --- /dev/null +++ b/logger_legacy.go @@ -0,0 +1,108 @@ +// Copyright © 2021 Kris Nóva +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logger + +import ( + "io" + "sync" +) + +// Legacy Logic for 0.1.0 +// +// Here we store the legacy (0.1.0) compatible configuration options +// that will eventually be deprecated. +// +var ( + + // Timestamps are used to toggle timestamps + // deprecated + Timestamps = true + + // TestMode is used for running + // the regression tests. + // deprecated + TestMode = false + + // Color is no longer used + // deprecated + Color = true + + // Level is the legacy log level + // + // 0 (Least verbose) + // 1 + // 2 + // 3 + // 4 (Most verbose) + // + // deprecated + Level = -1 +) + +var ( + testRaceMutex = sync.Mutex{} + annoyed = false +) + +// checkDeprecatedValues is a singleton +// that will only execute once. +// This will convert the legacy logger.Level +// to the new logger.BitwiseLevel +// +// LogEverything = +func checkDeprecatedValues() { + testRaceMutex.Lock() + defer testRaceMutex.Unlock() + if Level != -1 { + if !annoyed { + Deprecated("********") + Deprecated("***") + Deprecated("*") + Deprecated("logger.Level is deprecated. Use logger.BitwiseLevel") + Deprecated("*") + Deprecated("***") + Deprecated("********") + annoyed = true + } + if Level == 4 { + BitwiseLevel = LogDeprecated | LogAlways | LogSuccess | LogCritical | LogWarning | LogInfo | LogDebug + } else if Level == 3 { + BitwiseLevel = LogDeprecated | LogAlways | LogSuccess | LogCritical | LogWarning | LogInfo + } else if Level == 2 { + BitwiseLevel = LogDeprecated | LogAlways | LogSuccess | LogCritical | LogWarning + } else if Level == 1 { + BitwiseLevel = LogDeprecated | LogAlways | LogSuccess | LogCritical + } else if Level == 0 { + BitwiseLevel = LogDeprecated | LogAlways | LogSuccess + } else { + BitwiseLevel = LogDeprecated | LogEverything + } + } +} + +// legacyFindWriter will check if there is an io.Writer +// appended to the end of the arguments passed to the logger. +// +// deprecated +func legacyFindWriter(a ...interface{}) []interface{} { + if n := len(a); n > 0 { + // extract an io.Writer at the end of a + if newWriter, ok := a[n-1].(io.Writer); ok { + Writer = newWriter + a = a[0 : n-1] + } + } + return a +} diff --git a/logger_legacy_test.go b/logger_legacy_test.go new file mode 100644 index 0000000..1ac038b --- /dev/null +++ b/logger_legacy_test.go @@ -0,0 +1,158 @@ +// Copyright © 2021 Kris Nóva +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// +// Legacy Regression tests for 0.1.0 +// +// Note: @christopherhein added these tests and I am unsure if Amazon +// is still using the old io.Writer append() convention. +// Either way, I am keeping legacy (deprecated) support for it. +// +// All of these regression tests should still pass. +// +// ---------------------------------------------------------------------------- + +package logger + +import ( + "bytes" + "fmt" + "regexp" + "testing" +) + +const ( + format = "%v, %v, %v, all eyes on me!" + formatExp = `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.* \[%s\] \d, \d, \d, all eyes on me!` + formatWOTimeExp = `\[%s\] \d, \d, \d, all eyes on me!` +) + +var ( + a = []interface{}{1, 2, 3} +) + +func TestMain(m *testing.M) { + TestMode = true + m.Run() +} + +func TestAlwaysLegacy(t *testing.T) { + e, err := regexp.Compile(fmt.Sprintf(formatExp, PreAlways)) + g := captureLoggerOutput(Always, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Always should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +func TestCriticalLegacy(t *testing.T) { + Level = 1 + + e, err := regexp.Compile(fmt.Sprintf(formatExp, PreCritical)) + g := captureLoggerOutput(Critical, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Critical should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +func TestInfoLegacy(t *testing.T) { + Level = 3 + + e, err := regexp.Compile(fmt.Sprintf(formatExp, PreInfo)) + g := captureLoggerOutput(Info, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Info should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +func TestSuccessLegacy(t *testing.T) { + Level = 3 + + e, err := regexp.Compile(fmt.Sprintf(formatExp, PreSuccess)) + g := captureLoggerOutput(Success, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Success should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +func TestDebugLegacy(t *testing.T) { + Level = 4 + + e, err := regexp.Compile(fmt.Sprintf(formatExp, PreDebug)) + g := captureLoggerOutput(Debug, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Info should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +func TestWarningLegacy(t *testing.T) { + Level = 2 + + e, err := regexp.Compile(fmt.Sprintf(formatExp, PreWarning)) + g := captureLoggerOutput(Warning, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Info should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +func TestWithoutTimestampsLegacy(t *testing.T) { + Timestamps = false + e, err := regexp.Compile(fmt.Sprintf(formatWOTimeExp, PreAlways)) + g := captureLoggerOutput(Always, format, a) + + if err != nil { + t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) + } + + if !e.MatchString(g) { + t.Fatalf("Always should produce a pattern '%v' but produces: %v", e.String(), g) + } +} + +// captureLoggerOutput is used to test the log functions +func captureLoggerOutput(l LoggerFunc, format string, a []interface{}) string { + b := new(bytes.Buffer) + l(format, append(a, b)...) + return b.String() +} diff --git a/logger_test.go b/logger_test.go index eb4861f..0b7a678 100644 --- a/logger_test.go +++ b/logger_test.go @@ -1,146 +1,194 @@ -// Copyright © 2017 The Kubicorn Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package logger import ( "bytes" "fmt" - "regexp" "testing" ) const ( - format = "%v, %v, %v, all eyes on me!" - formatExp = `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.* \[%s\] \d, \d, \d, all eyes on me!` - formatWOTimeExp = `\[%s\] \d, \d, \d, all eyes on me!` + TestFormat = "%s %s %s money, success, fame glamour" ) var ( - a = []interface{}{1, 2, 3} + testA = []interface{}{"1", "2", "3"} ) -func TestMain(m *testing.M) { - TestMode = true - - m.Run() -} - -func TestAlways(t *testing.T) { - e, err := regexp.Compile(fmt.Sprintf(formatExp, AlwaysLabel)) - g := captureLoggerOutput(Always, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Always should produce a pattern '%v' but produces: %v", e.String(), g) +func TestAlwaysOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogAlways + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreAlways, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func TestCritical(t *testing.T) { - Level = 1 - - e, err := regexp.Compile(fmt.Sprintf(formatExp, CriticalLabel)) - g := captureLoggerOutput(Critical, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Critical should produce a pattern '%v' but produces: %v", e.String(), g) +func TestSuccessOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogSuccess + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreSuccess, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func TestInfo(t *testing.T) { - Level = 3 - - e, err := regexp.Compile(fmt.Sprintf(formatExp, InfoLabel)) - g := captureLoggerOutput(Info, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Info should produce a pattern '%v' but produces: %v", e.String(), g) +func TestDebugOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogDebug + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreDebug, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func TestSuccess(t *testing.T) { - Level = 3 - - e, err := regexp.Compile(fmt.Sprintf(formatExp, SuccessLabel)) - g := captureLoggerOutput(Success, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Success should produce a pattern '%v' but produces: %v", e.String(), g) +func TestInfoOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogInfo + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreInfo, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func TestDebug(t *testing.T) { - Level = 4 - - e, err := regexp.Compile(fmt.Sprintf(formatExp, DebugLabel)) - g := captureLoggerOutput(Debug, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Info should produce a pattern '%v' but produces: %v", e.String(), g) +func TestWarningOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogWarning + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreWarning, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func TestWarning(t *testing.T) { - Level = 2 - - e, err := regexp.Compile(fmt.Sprintf(formatExp, WarningLabel)) - g := captureLoggerOutput(Warning, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Info should produce a pattern '%v' but produces: %v", e.String(), g) +func TestCriticalOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogCritical + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreCritical, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func TestWithoutTimestamps(t *testing.T) { - Timestamps = false - e, err := regexp.Compile(fmt.Sprintf(formatWOTimeExp, AlwaysLabel)) - g := captureLoggerOutput(Always, format, a) - - if err != nil { - t.Fatalf("Failed to compile regexp '%v': %v", e.String(), err) - } - - if !e.MatchString(g) { - t.Fatalf("Always should produce a pattern '%v' but produces: %v", e.String(), g) +func TestDeprecatedOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogDeprecated + buffer := new(bytes.Buffer) + Writer = buffer + expected := Line(PreDeprecated, TestFormat, testA...) + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) } } -func captureLoggerOutput(l Logger, format string, a []interface{}) string { - b := new(bytes.Buffer) - l(format, append(a, b)...) - return b.String() +func TestEverything(t *testing.T) { + Level = -1 + BitwiseLevel = LogEverything + buffer := new(bytes.Buffer) + Writer = buffer + cases := []string{PreDeprecated, PreDebug, PreInfo, PreWarning, PreCritical, PreSuccess, PreAlways} + expected := "" + for _, c := range cases { + expected = fmt.Sprintf("%s%s", expected, Line(c, TestFormat, testA...)) + } + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) + } +} + +func TestAlwaysCriticalDebugOnly(t *testing.T) { + Level = -1 + BitwiseLevel = LogAlways | LogCritical | LogDebug + buffer := new(bytes.Buffer) + Writer = buffer + cases := []string{PreDebug, PreCritical, PreAlways} + expected := "" + for _, c := range cases { + expected = fmt.Sprintf("%s%s", expected, Line(c, TestFormat, testA...)) + } + Deprecated(TestFormat, testA...) + Debug(TestFormat, testA...) + Info(TestFormat, testA...) + Warning(TestFormat, testA...) + Critical(TestFormat, testA...) + Success(TestFormat, testA...) + Always(TestFormat, testA...) + actual := buffer.String() + if expected != actual { + t.Errorf("Expected (%s) Actual (%s)", expected, actual) + } } diff --git a/screenshot.png b/screenshot.png index 3aee64d..af9043c 100644 Binary files a/screenshot.png and b/screenshot.png differ