From f6d6f945a87b02eb45c37c625ea916d767c692c1 Mon Sep 17 00:00:00 2001 From: Kris Nova Date: Tue, 27 Nov 2018 14:47:56 -0800 Subject: [PATCH] Porting from kubicorn --- README.md | 43 ++++++++++- example/example.go | 38 ++++++++++ example/fabulous.go | 33 ++++++++ logger.go | 179 ++++++++++++++++++++++++++++++++++++++++++++ logger_test.go | 146 ++++++++++++++++++++++++++++++++++++ 5 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 example/example.go create mode 100644 example/fabulous.go create mode 100644 logger.go create mode 100644 logger_test.go diff --git a/README.md b/README.md index 9aadf08..e0cd089 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,43 @@ # logger -Moving my Go logger out into it's own repository + +Ported from it's [original location](https://github.com/kubicorn/kubicorn/tree/master/pkg/logger) in the Kubicorn code base. + +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) +} + +``` diff --git a/example/example.go b/example/example.go new file mode 100644 index 0000000..ca27826 --- /dev/null +++ b/example/example.go @@ -0,0 +1,38 @@ +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 new file mode 100644 index 0000000..9c6a305 --- /dev/null +++ b/example/fabulous.go @@ -0,0 +1,33 @@ +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/logger.go b/logger.go new file mode 100644 index 0000000..9e7423e --- /dev/null +++ b/logger.go @@ -0,0 +1,179 @@ +// Copyright © 2017 +// +// 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 ( + "fmt" + "io" + "os" + "strings" + "time" + + lol "github.com/kris-nova/lolgopher" + + "github.com/fatih/color" +) + +type Logger func(format string, a ...interface{}) + +const ( + AlwaysLabel = "✿" + CriticalLabel = "✖" + DebugLabel = "▶" + InfoLabel = "ℹ" + SuccessLabel = "✔" + WarningLabel = "!" +) + +var ( + Level = 2 + Color = true + Fabulous = false + FabulousWriter = lol.NewLolWriter() + FabulousTrueWriter = lol.NewTruecolorLolWriter() + TestMode = false +) + +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.Fprintf(w, s) +} + +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.Fprintf(w, s) + } +} + +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.Fprintf(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.Fprintf(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.Fprintf(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.Fprintf(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 { + t := time.Now() + rfct := t.Format(time.RFC3339) + if !strings.Contains(format, "\n") { + format = fmt.Sprintf("%s%s", format, "\n") + } + return fmt.Sprintf("%s [%s] %s", rfct, label, format) +} \ No newline at end of file diff --git a/logger_test.go b/logger_test.go new file mode 100644 index 0000000..7b5c18b --- /dev/null +++ b/logger_test.go @@ -0,0 +1,146 @@ +// 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" + "strings" + "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!` +) + +var ( + a = []interface{}{1, 2, 3} +) + +func TestMain(m *testing.M) { + TestMode = true + + m.Run() +} + +func ExampleTestLog() { + Log(format, a...) + // Output: 1, 2, 3, all eyes on me! +} + +func TestLog(t *testing.T) { + e := fmt.Sprintf(format, a...) + g := captureLoggerOutput(Log, format, a) + + if strings.Compare(e, g) != 0 { + t.Fatalf("Log should produce '%v' but produces: %v", e, g) + } +} + +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 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 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 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 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 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 captureLoggerOutput(l Logger, format string, a []interface{}) string { + b := new(bytes.Buffer) + l(format, append(a, b)...) + return b.String() +} \ No newline at end of file