From 2b0c149aeb3e4ec411065cf243a842d676134d98 Mon Sep 17 00:00:00 2001 From: Thomas Lokshall Date: Wed, 9 Dec 2015 08:06:37 +0100 Subject: [PATCH] Restructure API --- README.md | 72 +++----- doc.go | 2 + doc_test.go | 63 +++++++ level.go | 38 ++++ level_test.go | 25 +++ log.go | 48 +++++ log/log.go | 52 ------ log/log_test.go | 45 ----- log/logger_test.go | 358 ------------------------------------- log_test.go | 41 +++++ log/logger.go => logger.go | 98 +++++----- logger_test.go | 88 +++++++++ wercker.yml | 4 + 13 files changed, 379 insertions(+), 555 deletions(-) create mode 100644 doc.go create mode 100644 doc_test.go create mode 100644 level.go create mode 100644 level_test.go create mode 100644 log.go delete mode 100644 log/log.go delete mode 100644 log/log_test.go delete mode 100644 log/logger_test.go create mode 100644 log_test.go rename log/logger.go => logger.go (51%) create mode 100644 logger_test.go diff --git a/README.md b/README.md index 34e72c3..3b85cb2 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,46 @@ -go-log - Simple logging library -=============================== +log - Simple logging library +============================ -[![wercker status](https://app.wercker.com/status/c98a6d8f01bffef5bca40c3563347dba/m "wercker status")](https://app.wercker.com/project/bykey/c98a6d8f01bffef5bca40c3563347dba) +[![wercker status](https://app.wercker.com/status/054f2a1e2351df9bc41d07889f2bcf36/s/master "wercker status")](https://app.wercker.com/project/bykey/054f2a1e2351df9bc41d07889f2bcf36) +[![Coverage Status](https://coveralls.io/repos/zhevron/log/badge.svg?branch=master&service=github)](https://coveralls.io/github/zhevron/log?branch=master) +[![GoDoc](https://godoc.org/github.com/zhevron/log?status.svg)](https://godoc.org/github.com/zhevron/log) -[![Coverage Status](https://coveralls.io/repos/zhevron/go-log/badge.svg?branch=HEAD)](https://coveralls.io/r/zhevron/go-log?branch=HEAD) -[![GoDoc](https://godoc.org/gopkg.in/zhevron/go-log.v0/log?status.svg)](https://godoc.org/gopkg.in/zhevron/go-log.v0/log) - -**go-log** is a simple logging library for [Go](https://golang.org/). - -For package documentation, refer to the GoDoc badge above. - -## Installation - -``` -go get gopkg.in/zhevron/go-log.v0/log -``` +**log** is a simple logging library for [Google Go](https://golang.org/). +For full package documentation, see the GoDoc link above. ## Usage ### Log to file ```go -package main - -import ( - "os" - - "gopkg.in/zhevron/go-log.v0/log" -) - -func main() { - // Open the log file. - f, err := os.Create("myLogFile.log") - if err != nil { - panic(err) - } - defer f.Close() +// Create and open a file. +f, err := os.Create("myLogFile.log") +if err != nil { + panic(err) +} +defer f.Close() - // Create a new logger. - logger := log.NewLogger("MyLogger", log.Info) +// Add the file as an output. +log.AddOutput(f) - // Add the file as an output. - logger.Output(f) +// Create a new logger. +logger := log.NewLogger("MyLogger", log.Info) - logger.Info("This message will appear in the log file") -} +logger.Info("This message will appear in the log file") ``` ### Log to stdout ```go -package main +// Add stdout as an output. +log.AddOutput(os.Stdout) -import "gopkg.in/zhevron/go-log.v0/log" +// Create a new logger. +logger := log.NewLogger("MyLogger", log.Info) -func main() { - // Create a new logger. - logger := log.NewLogger("MyLogger", log.Info) - - // Add stdout as an output. - logger.Output(os.Stdout) - - logger.Info("This message will appear in stdout") -} +logger.Info("This message will appear in stdout") ``` ## License -**go-log** is licensed under the [MIT license](http://opensource.org/licenses/MIT). +**log** is licensed under the [MIT license](http://opensource.org/licenses/MIT). diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..9a0db79 --- /dev/null +++ b/doc.go @@ -0,0 +1,2 @@ +// Package log provides a simple logging interface. +package log diff --git a/doc_test.go b/doc_test.go new file mode 100644 index 0000000..abbd29f --- /dev/null +++ b/doc_test.go @@ -0,0 +1,63 @@ +package log + +import ( + "os" + + "github.com/zhevron/log" +) + +var logger = NewLogger("ExampleLogger", Info) + +func ExampleAddOutput() { + f, err := os.Create("myLogFile.log") + if err != nil { + panic(err) + } + defer f.Close() + log.AddOutput(f) +} + +func ExampleLoggerDebug() { + logger.Debug("This is a debug message") +} + +func ExampleLoggerDebugf() { + msg := "This is a debug message" + logger.Debugf("%s", msg) +} + +func ExampleLoggerInfo() { + logger.Info("This is an info message") +} + +func ExampleLoggerInfof() { + msg := "This is an info message" + logger.Infof("%s", msg) +} + +func ExampleLoggerWarning() { + logger.Warning("This is a warning message") +} + +func ExampleLoggerWarningf() { + msg := "This is a warning message" + logger.Warningf("%s", msg) +} + +func ExampleLoggerError() { + logger.Error("This is an error message") +} + +func ExampleLoggerErrorf() { + msg := "This is an error message" + logger.Errorf("%s", msg) +} + +func ExampleLoggerFatal() { + logger.Fatal("This is a fatal message") +} + +func ExampleLoggerFatalf() { + msg := "This is a fatal message" + logger.Fatalf("%s", msg) +} diff --git a/level.go b/level.go new file mode 100644 index 0000000..b156f7d --- /dev/null +++ b/level.go @@ -0,0 +1,38 @@ +package log + +type level uint8 + +const ( + // Debug is used for messages related to debugging. + Debug level = iota + // Info is used for informational messages. + Info + // Warning is used for warnings. + Warning + // Error is used for non-fatal errors. + Error + // Fatal used for fatal errors. + Fatal +) + +func (l level) String() string { + switch l { + case Debug: + return "DEBUG" + + case Info: + return "INFO" + + case Warning: + return "WARNING" + + case Error: + return "ERROR" + + case Fatal: + return "FATAL" + + default: + return "UNKNOWN" + } +} diff --git a/level_test.go b/level_test.go new file mode 100644 index 0000000..fa74a5d --- /dev/null +++ b/level_test.go @@ -0,0 +1,25 @@ +package log + +import ( + "testing" + + "github.com/zhevron/match" +) + +var levelTests = []struct { + in level + out string +}{ + {Debug, "DEBUG"}, + {Info, "INFO"}, + {Warning, "WARNING"}, + {Error, "ERROR"}, + {Fatal, "FATAL"}, + {level(255), "UNKNOWN"}, +} + +func TestLevelString(t *testing.T) { + for _, tt := range levelTests { + match.Equals(t, tt.in.String(), tt.out) + } +} diff --git a/log.go b/log.go new file mode 100644 index 0000000..5ad173c --- /dev/null +++ b/log.go @@ -0,0 +1,48 @@ +package log + +import ( + "io" + "sync" +) + +// DefaultTimestampFormat defines the timestamp format all new loggers will be +// created with. You can override this per logger instance by setting +// Logger.TimestampFormat. +var DefaultTimestampFormat = "2006-01-02T15:04:05-07:00" + +// DefaultShowFileAndLineNumber defines whether the ShowFileAndLineNumber +// property on new Logger instances is enabled by default. +var DefaultShowFileAndLineNumber = false + +// DefaultUseUTC defines whether the UseUTC property on new Logger instances is +// enabled by default. +var DefaultUseUTC = false + +type writer struct { + writer io.Writer + mutex sync.Mutex +} + +var loggers []*Logger + +var writers []*writer + +// AddOutput adds one or more io.Writer instances as output sinks for the +// library. If a writer that has previously been added is passed, it will be +// ignored. +func AddOutput(ioWriters ...io.Writer) { + for i := range ioWriters { + for j := range writers { + if ioWriters[i] == writers[j].writer { + ioWriters = append(ioWriters[:i], ioWriters[i+1:]...) + } + } + } + + var newWriters []*writer + for i := range ioWriters { + newWriters = append(newWriters, &writer{writer: ioWriters[i]}) + } + + writers = append(writers, newWriters...) +} diff --git a/log/log.go b/log/log.go deleted file mode 100644 index 073b208..0000000 --- a/log/log.go +++ /dev/null @@ -1,52 +0,0 @@ -// Package log provides a simple logging interface. -package log - -// Level identifies a log level. -type Level int - -// Log levels. -const ( - None Level = 0 // Do not send log messages to sinks. - Debug Level = 1 // Use for messages related to debugging. - Info Level = 2 // Use for informational messages. - Warning Level = 3 // Use for warnings. - Error Level = 4 // Use for non-fatal errors. - Fatal Level = 5 // Use for fatal errors. -) - -// loggers contain all the created Logger objects. -var loggers []*Logger - -// GetLogger will return the logger with the given name. -// If the logger does not exist, one will be created a default level of Info. -func GetLogger(name string) *Logger { - for i := range loggers { - if loggers[i].Name == name { - return loggers[i] - } - } - - return NewLogger(name, Info) -} - -// String returns the string version of a log level. -func (l Level) String() string { - switch l { - case Debug: - return "DEBUG" - - case Info: - return "INFO" - - case Warning: - return "WARNING" - - case Error: - return "ERROR" - - case Fatal: - return "FATAL" - } - - return "" -} diff --git a/log/log_test.go b/log/log_test.go deleted file mode 100644 index b581b4a..0000000 --- a/log/log_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package log - -import "testing" - -func TestGetLogger(t *testing.T) { - l := NewLogger("Test", Info) - logger := GetLogger("Test") - - if logger != l { - t.Errorf("expected %#q, got %#q", l, logger) - } -} - -func TestGetLogger_New(t *testing.T) { - l := GetLogger("Test2") - - if l == nil { - t.Error("expected non-nil, got nil") - } - - if l.Name != "Test2" { - t.Errorf("expected %#q, got %#q", "Test", l.Name) - } - - if l.Level != Info { - t.Errorf("expected %#q, got %#q", Info, l.Level) - } -} - -func TestLevelString(t *testing.T) { - m := map[Level]string{ - None: "", - Debug: "DEBUG", - Info: "INFO", - Warning: "WARNING", - Error: "ERROR", - Fatal: "FATAL", - } - - for l, s := range m { - if l.String() != s { - t.Errorf("expected %#q, got %#q", s, l.String()) - } - } -} diff --git a/log/logger_test.go b/log/logger_test.go deleted file mode 100644 index eac0bae..0000000 --- a/log/logger_test.go +++ /dev/null @@ -1,358 +0,0 @@ -package log - -import ( - "bytes" - "strings" - "testing" - "time" -) - -func TestLoggerOutput(t *testing.T) { - l := NewLogger("Test", Info) - - buf := new(bytes.Buffer) - l.Output(buf) - - if len(l.writers) != 1 { - t.Errorf("expected 1, got %d", len(l.writers)) - } - - if l.writers[0].writer != buf { - t.Errorf("expected %#q, got %#q", buf, l.writers[0].writer) - } -} - -func TestLoggerOutput_Duplicate(t *testing.T) { - l := NewLogger("Test", Info) - - buf := new(bytes.Buffer) - l.Output(buf) - l.Output(buf) - - if len(l.writers) != 1 { - t.Errorf("expected 1, got %d", len(l.writers)) - } -} - -func TestLoggerOutput_Multiple(t *testing.T) { - l := NewLogger("Test", Info) - - buf1 := new(bytes.Buffer) - buf2 := new(bytes.Buffer) - l.Output(buf1, buf2) - - if len(l.writers) != 2 { - t.Errorf("expected 2, got %d", len(l.writers)) - } -} - -func TestLoggerWrite(t *testing.T) { - l := NewLogger("Test", Info) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.write(msg, 0) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } -} - -func TestLoggerWrite_IncludeTimeStamp(t *testing.T) { - l := NewLogger("Test", Info) - l.IncludeTimeStamp = true - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.write(msg, 0) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - now := time.Now().Format("2006-01-02") - if !strings.Contains(str, now) { - t.Errorf("did not find %#q in strfer %#q", now, str) - } -} - -func TestLoggerWrite_ShowFileAndLineNumber(t *testing.T) { - l := NewLogger("Test", Info) - l.ShowFileAndLineNumber = true - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.write(msg, 1) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - file := "logger_test.go" - if !strings.Contains(str, file) { - t.Errorf("did not find %#q in strfer %#q", file, str) - } -} - -func TestLoggerWrite_ShowFileAndLineNumber_Fail(t *testing.T) { - l := NewLogger("Test", Info) - l.ShowFileAndLineNumber = true - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.write(msg, 20) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - file := "" - if !strings.Contains(str, file) { - t.Errorf("did not find %#q in strfer %#q", file, str) - } -} - -func TestLoggerWrite_UseUTC(t *testing.T) { - l := NewLogger("Test", Info) - l.UseUTC = true - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.write(msg, 0) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - now := time.Now().UTC().Format("2006-01-02") - if !strings.Contains(str, now) { - t.Errorf("did not find %#q in strfer %#q", now, str) - } -} - -func TestLoggerDebug(t *testing.T) { - l := NewLogger("Test", Debug) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Debug(msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Debug.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerDebugf(t *testing.T) { - l := NewLogger("Test", Debug) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Debugf("%s", msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Debug.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerInfo(t *testing.T) { - l := NewLogger("Test", Info) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Info(msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Info.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerInfof(t *testing.T) { - l := NewLogger("Test", Info) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Infof("%s", msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Info.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerWarning(t *testing.T) { - l := NewLogger("Test", Warning) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Warning(msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Warning.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerWarningf(t *testing.T) { - l := NewLogger("Test", Warning) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Warningf("%s", msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Warning.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerError(t *testing.T) { - l := NewLogger("Test", Error) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Error(msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Error.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerErrorf(t *testing.T) { - l := NewLogger("Test", Error) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - l.Errorf("%s", msg) - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Errorf("did not find %#q in strfer %#q", msg, str) - } - - level := Error.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } -} - -func TestLoggerFatal(t *testing.T) { - l := NewLogger("Test", Fatal) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - defer func() { - if r := recover(); r == nil { - t.Error("did not recover from panic") - } - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Fatal("did not find msg in strfer") - } - - level := Fatal.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } - }() - - l.Fatal(msg) -} - -func TestLoggerFatalf(t *testing.T) { - l := NewLogger("Test", Fatal) - - buf := new(bytes.Buffer) - l.Output(buf) - - msg := "Testing log message" - defer func() { - if r := recover(); r == nil { - t.Error("did not recover from panic") - } - - str := string(buf.Bytes()) - if !strings.Contains(str, msg) { - t.Fatal("did not find msg in strfer") - } - - level := Fatal.String() - if !strings.Contains(str, level) { - t.Errorf("did not find %#q in strfer %#q", l, str) - } - }() - - l.Fatalf("%s", msg) -} diff --git a/log_test.go b/log_test.go new file mode 100644 index 0000000..03f11a9 --- /dev/null +++ b/log_test.go @@ -0,0 +1,41 @@ +package log + +import ( + "bytes" + "os" + "testing" + + "github.com/zhevron/match" +) + +var buffer = new(bytes.Buffer) + +func TestMain(m *testing.M) { + AddOutput(buffer) + retCode := m.Run() + os.Exit(retCode) +} + +func TestAddOutput(t *testing.T) { + buf := new(bytes.Buffer) + AddOutput(buf) + match.Equals(t, len(writers), 2).Fatal() + match.Equals(t, writers[1].writer, buf).Fatal() + writers = writers[:len(writers)-1] +} + +func TestLoggerAddOutput_Duplicate(t *testing.T) { + buf := new(bytes.Buffer) + AddOutput(buf) + AddOutput(buf) + match.Equals(t, len(writers), 2).Fatal() + writers = writers[:len(writers)-1] +} + +func TestLoggerAddOutput_Multiple(t *testing.T) { + buf1 := new(bytes.Buffer) + buf2 := new(bytes.Buffer) + AddOutput(buf1, buf2) + match.Equals(t, len(writers), 3).Fatal() + writers = writers[:len(writers)-2] +} diff --git a/log/logger.go b/logger.go similarity index 51% rename from log/logger.go rename to logger.go index 1b93dd0..2b51e9b 100644 --- a/log/logger.go +++ b/logger.go @@ -2,113 +2,107 @@ package log import ( "fmt" - "io" "runtime" - "sync" "time" ) -type writer struct { - writer io.Writer - mutex sync.Mutex +// Logger defines a named logging instance. It is used to indicate the origin +// of a logged line in the output. +type Logger struct { + Name string // The logger name + MinimumLevel level // Minimum log level + TimestampFormat string // The format of the logging timestamp + IncludeTimeStamp bool // Whether to include the current timestamp + ShowFileAndLineNumber bool // Whether to show file and line number + UseUTC bool // Whether the timestamp is logged as UTC } -type Logger struct { - Name string // The logger name - Level Level // Minimum log level - TimestampFormat string // The format of the logging timestamp - IncludeTimeStamp bool // Whether to include the current timestamp - ShowFileAndLineNumber bool // Whether to show file and line number - UseUTC bool // Whether the timestamp is logged as UTC - writers []*writer // The io.Writer instances to log to +// GetLogger will return the Logger instance with the given name. +// If the Logger does not exist, one will be created a minimum level of Info. +func GetLogger(name string) *Logger { + for i := range loggers { + if loggers[i].Name == name { + return loggers[i] + } + } + + return NewLogger(name, Info) } -func NewLogger(name string, level Level) *Logger { +// NewLogger creates a new Logger instance with the given name and minimum +// log level. If there's already an instance with the given name, +func NewLogger(name string, minimumLevel level) *Logger { l := &Logger{ Name: name, - Level: level, - TimestampFormat: "2006-01-02T15:04:05-07:00", + MinimumLevel: minimumLevel, + TimestampFormat: DefaultTimestampFormat, IncludeTimeStamp: true, - ShowFileAndLineNumber: false, - UseUTC: false, + ShowFileAndLineNumber: DefaultShowFileAndLineNumber, + UseUTC: DefaultUseUTC, } loggers = append(loggers, l) return l } -func (l *Logger) Output(writers ...io.Writer) { - for i := range writers { - for j := range l.writers { - if writers[i] == l.writers[j].writer { - writers = append(writers[:i], writers[i+1:]...) - } - } - } - - var w []*writer - for i := range writers { - w = append(w, &writer{writer: writers[i]}) - } - - l.writers = append(l.writers, w...) -} - // Debug logs a message with the LevelDebug level. func (l *Logger) Debug(message string) { - l.write(message, 2) + l.write(message, Debug, 2) } // Debugf logs a formatted message with the LevelDebug level. func (l *Logger) Debugf(message string, format ...interface{}) { - l.write(fmt.Sprintf(message, format...), 2) + l.write(fmt.Sprintf(message, format...), Debug, 2) } // Info logs a message with the LevelInformation level. func (l *Logger) Info(message string) { - l.write(message, 2) + l.write(message, Info, 2) } // Infof logs a formatted message with the LevelInformation level. func (l *Logger) Infof(message string, format ...interface{}) { - l.write(fmt.Sprintf(message, format...), 2) + l.write(fmt.Sprintf(message, format...), Info, 2) } // Warning logs a message with the LevelWarning level. func (l *Logger) Warning(message string) { - l.write(message, 2) + l.write(message, Warning, 2) } // Warningf logs a formatted message with the LevelWarning level. func (l *Logger) Warningf(message string, format ...interface{}) { - l.write(fmt.Sprintf(message, format...), 2) + l.write(fmt.Sprintf(message, format...), Warning, 2) } // Error logs a message with the LevelError level. func (l *Logger) Error(message string) { - l.write(message, 2) + l.write(message, Error, 2) } // Errorf logs a formatted message with the LevelError level. func (l *Logger) Errorf(message string, format ...interface{}) { - l.write(fmt.Sprintf(message, format...), 2) + l.write(fmt.Sprintf(message, format...), Error, 2) } // Fatal logs a message with the LevelFatal level and panics. func (l *Logger) Fatal(message string) { - l.write(message, 2) + l.write(message, Fatal, 2) panic(message) } // Fatalf logs a formatted message with the LevelFatal level and panics. func (l *Logger) Fatalf(message string, format ...interface{}) { - l.write(fmt.Sprintf(message, format...), 2) + l.write(fmt.Sprintf(message, format...), Fatal, 2) panic(fmt.Sprintf(message, format...)) } -// write loops all the writers on a Logger and writes the string to them. -func (l *Logger) write(message string, calldepth int) { - prefix := fmt.Sprintf("[%s] [%s]", l.Level.String(), l.Name) +func (l *Logger) write(message string, logLevel level, calldepth int) { + if logLevel < l.MinimumLevel { + return + } + + prefix := fmt.Sprintf("[%s] [%s]", logLevel.String(), l.Name) if l.IncludeTimeStamp { t := time.Now() @@ -136,9 +130,9 @@ func (l *Logger) write(message string, calldepth int) { } b := []byte(message) - for i := range l.writers { - l.writers[i].mutex.Lock() - l.writers[i].writer.Write(b) - l.writers[i].mutex.Unlock() + for i := range writers { + writers[i].mutex.Lock() + writers[i].writer.Write(b) + writers[i].mutex.Unlock() } } diff --git a/logger_test.go b/logger_test.go new file mode 100644 index 0000000..3f735bb --- /dev/null +++ b/logger_test.go @@ -0,0 +1,88 @@ +package log + +import ( + "testing" + "time" + + "github.com/zhevron/match" +) + +var loggerWriteTests = []struct { + in string + out string + level level + calldepth int +}{ + {"Test Message", "Test Message", Info, 1}, + {"Test Message", "", Debug, 1}, + {"Test Message", time.Now().UTC().Format("2006-01-02"), Info, 1}, + {"Test Message", "logger_test.go", Info, 1}, + {"Test Message", "", Info, 10}, +} + +func TestGetLogger(t *testing.T) { + l := NewLogger("Test", Info) + match.Equals(t, GetLogger("Test"), l) + l = GetLogger("Test2") + match.IsNotNil(t, l) + match.Equals(t, l.Name, "Test2") + match.Equals(t, l.MinimumLevel, Info) +} + +func TestNewLogger(t *testing.T) { + l := NewLogger("Test3", Info) + match.IsNotNil(t, l) +} + +func TestLoggerWrite(t *testing.T) { + logger := NewLogger("TestLoggerWrite", Info) + logger.UseUTC = true + logger.ShowFileAndLineNumber = true + for _, tt := range loggerWriteTests { + buffer.Reset() + logger.write(tt.in, tt.level, tt.calldepth) + match.Contains(t, string(buffer.Bytes()), tt.out) + } +} + +func TestLoggerLevels(t *testing.T) { + logger := NewLogger("TestLoggerLevels", Debug) + buffer.Reset() + logger.Debug("STR_" + Debug.String()) + logger.Debugf("FORMAT_" + Debug.String()) + match.Contains(t, string(buffer.Bytes()), "STR_"+Debug.String()).Contains("FORMAT_" + Debug.String()) + buffer.Reset() + logger.Info("STR_" + Info.String()) + logger.Infof("FORMAT_" + Info.String()) + match.Contains(t, string(buffer.Bytes()), "STR_"+Info.String()).Contains("FORMAT_" + Info.String()) + buffer.Reset() + logger.Warning("STR_" + Warning.String()) + logger.Warningf("FORMAT_" + Warning.String()) + match.Contains(t, string(buffer.Bytes()), "STR_"+Warning.String()).Contains("FORMAT_" + Warning.String()) + buffer.Reset() + logger.Error("STR_" + Error.String()) + logger.Errorf("FORMAT_" + Error.String()) + match.Contains(t, string(buffer.Bytes()), "STR_"+Error.String()).Contains("FORMAT_" + Error.String()) +} + +func TestLoggerFatal(t *testing.T) { + logger := NewLogger("TestLoggerFatal", Info) + defer func() { + r := recover() + match.IsNotNil(t, r) + match.Contains(t, string(buffer.Bytes()), "STR_"+Fatal.String()) + }() + buffer.Reset() + logger.Fatal("STR_" + Fatal.String()) +} + +func TestLoggerFatalf(t *testing.T) { + logger := NewLogger("TestLoggerFatalf", Info) + defer func() { + r := recover() + match.IsNotNil(t, r) + match.Contains(t, string(buffer.Bytes()), "FORMAT_"+Fatal.String()) + }() + buffer.Reset() + logger.Fatalf("FORMAT_" + Fatal.String()) +} diff --git a/wercker.yml b/wercker.yml index 98ef00d..6b97b95 100644 --- a/wercker.yml +++ b/wercker.yml @@ -4,6 +4,10 @@ build: steps: - setup-go-workspace - golint + - script: + name: go get + code: | + go get -t ./... - script: name: go build code: |