From 6c895096e850ba3210087d435d3867a0d4c4a68f Mon Sep 17 00:00:00 2001 From: Simon Eskildsen Date: Sun, 23 Feb 2014 19:50:42 -0500 Subject: [PATCH] split into multiple files --- entry.go | 191 ++++++++++++++++++++++++++++++++ logger.go | 121 +++++++++++++++++++++ logrus.go | 319 +----------------------------------------------------- 3 files changed, 314 insertions(+), 317 deletions(-) create mode 100644 entry.go create mode 100644 logger.go diff --git a/entry.go b/entry.go new file mode 100644 index 0000000..a492078 --- /dev/null +++ b/entry.go @@ -0,0 +1,191 @@ +package logrus + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "time" + + "github.com/tobi/airbrake-go" +) + +type Entry struct { + logger *Logger + Data Fields +} + +func NewEntry(logger *Logger) *Entry { + return &Entry{ + logger: logger, + // Default is three fields, give a little extra room. Shouldn't hurt the + // scale. + Data: make(Fields, 5), + } +} + +// TODO: Other formats? +func (entry *Entry) Reader() (read *bytes.Buffer, err error) { + var serialized []byte + + if Environment == "production" { + serialized, err = json.Marshal(entry.Data) + } else { + // TODO: Pretty-print more by coloring when stdout is a tty + serialized, err = json.MarshalIndent(entry.Data, "", " ") + } + + if err != nil { + return nil, err + } + + serialized = append(serialized, '\n') + + return bytes.NewBuffer(serialized), nil +} + +func (entry *Entry) WithField(key string, value interface{}) *Entry { + entry.Data[key] = value + return entry +} + +func (entry *Entry) WithFields(fields Fields) *Entry { + for key, value := range fields { + entry.WithField(key, value) + } + + return entry +} + +func (entry *Entry) log(level string, msg string) { + // TODO: Is the default format output from String() the one we want? + entry.Data["timestamp"] = time.Now().String() + entry.Data["level"] = level + // TODO: Is this the best name? + entry.Data["msg"] = msg + + reader, err := entry.Reader() + if err != nil { + entry.logger.Panicln("Failed to marshal JSON ", err.Error()) + } + + // Send HTTP request in a goroutine in warning environment to not halt the + // main thread. It's sent before logging due to panic. + if level == "warning" { + // TODO: new() should spawn an airbrake goroutine and this should send to + // that channel. This prevent us from spawning hundreds of goroutines in a + // hot code path generating a warning. + go entry.airbrake(reader.String()) + } else if level == "fatal" || level == "panic" { + entry.airbrake(reader.String()) + } + + if level == "panic" { + panic(reader.String()) + } else { + entry.logger.mu.Lock() + io.Copy(entry.logger.Out, reader) + entry.logger.mu.Unlock() + } +} + +func (entry *Entry) Debug(args ...interface{}) { + if Level >= LevelDebug { + entry.log("debug", fmt.Sprint(args...)) + } +} + +func (entry *Entry) Info(args ...interface{}) { + if Level >= LevelInfo { + entry.log("info", fmt.Sprint(args...)) + } +} + +func (entry *Entry) Print(args ...interface{}) { + if Level >= LevelInfo { + entry.log("info", fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warning(args ...interface{}) { + if Level >= LevelWarning { + entry.log("warning", fmt.Sprint(args...)) + } +} + +func (entry *Entry) Fatal(args ...interface{}) { + if Level >= LevelFatal { + entry.log("fatal", fmt.Sprint(args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panic(args ...interface{}) { + if Level >= LevelPanic { + entry.log("panic", fmt.Sprint(args...)) + } +} + +// Entry Printf family functions + +func (entry *Entry) Debugf(format string, args ...interface{}) { + entry.Debug(fmt.Sprintf(format, args...)) +} + +func (entry *Entry) Infof(format string, args ...interface{}) { + entry.Info(fmt.Sprintf(format, args...)) +} + +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Print(fmt.Sprintf(format, args...)) +} + +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warning(fmt.Sprintf(format, args...)) +} + +func (entry *Entry) Fatalf(format string, args ...interface{}) { + entry.Fatal(fmt.Sprintf(format, args...)) +} + +func (entry *Entry) Panicf(format string, args ...interface{}) { + entry.Panic(fmt.Sprintf(format, args...)) +} + +// Entry Println family functions + +func (entry *Entry) Debugln(args ...interface{}) { + entry.Debug(fmt.Sprintln(args...)) +} + +func (entry *Entry) Infoln(args ...interface{}) { + entry.Info(fmt.Sprintln(args...)) +} + +func (entry *Entry) Println(args ...interface{}) { + entry.Print(fmt.Sprintln(args...)) +} + +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warning(fmt.Sprintln(args...)) +} + +func (entry *Entry) Fatalln(args ...interface{}) { + entry.Fatal(fmt.Sprintln(args...)) +} + +func (entry *Entry) Panicln(args ...interface{}) { + entry.Panic(fmt.Sprintln(args...)) +} + +func (entry *Entry) airbrake(exception string) { + err := airbrake.Notify(errors.New(exception)) + if err != nil { + entry.logger.WithFields(Fields{ + "source": "airbrake", + "endpoint": airbrake.Endpoint, + }).Infof("Failed to send exception to Airbrake") + } +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..cf3154d --- /dev/null +++ b/logger.go @@ -0,0 +1,121 @@ +package logrus + +import ( + "io" + "os" + "strings" + "sync" + + "github.com/tobi/airbrake-go" +) + +type Logger struct { + Out io.Writer + mu sync.Mutex +} + +func New() *Logger { + environment := strings.ToLower(os.Getenv("ENV")) + if environment == "" { + environment = "development" + } + + if airbrake.Environment == "" { + airbrake.Environment = environment + } + + return &Logger{ + Out: os.Stdout, // Default to stdout, change it if you want. + } +} + +func (logger *Logger) WithField(key string, value interface{}) *Entry { + entry := NewEntry(logger) + entry.WithField(key, value) + return entry +} + +func (logger *Logger) WithFields(fields Fields) *Entry { + entry := NewEntry(logger) + entry.WithFields(fields) + return entry +} + +// Entry Print family functions +// Logger Printf family functions + +func (logger *Logger) Debugf(format string, args ...interface{}) { + NewEntry(logger).Debugf(format, args...) +} + +func (logger *Logger) Infof(format string, args ...interface{}) { + NewEntry(logger).Infof(format, args...) +} + +func (logger *Logger) Printf(format string, args ...interface{}) { + NewEntry(logger).Printf(format, args...) +} + +func (logger *Logger) Warningf(format string, args ...interface{}) { + NewEntry(logger).Warningf(format, args...) +} + +func (logger *Logger) Fatalf(format string, args ...interface{}) { + NewEntry(logger).Fatalf(format, args...) +} + +func (logger *Logger) Panicf(format string, args ...interface{}) { + NewEntry(logger).Panicf(format, args...) +} + +// Logger Print family functions + +func (logger *Logger) Debug(args ...interface{}) { + NewEntry(logger).Debug(args...) +} + +func (logger *Logger) Info(args ...interface{}) { + NewEntry(logger).Info(args...) +} + +func (logger *Logger) Print(args ...interface{}) { + NewEntry(logger).Print(args...) +} + +func (logger *Logger) Warning(args ...interface{}) { + NewEntry(logger).Warning(args...) +} + +func (logger *Logger) Fatal(args ...interface{}) { + NewEntry(logger).Fatal(args...) +} + +func (logger *Logger) Panic(args ...interface{}) { + NewEntry(logger).Panic(args...) +} + +// Logger Println family functions + +func (logger *Logger) Debugln(args ...interface{}) { + NewEntry(logger).Debugln(args...) +} + +func (logger *Logger) Infoln(args ...interface{}) { + NewEntry(logger).Infoln(args...) +} + +func (logger *Logger) Println(args ...interface{}) { + NewEntry(logger).Println(args...) +} + +func (logger *Logger) Warningln(args ...interface{}) { + NewEntry(logger).Warningln(args...) +} + +func (logger *Logger) Fatalln(args ...interface{}) { + NewEntry(logger).Fatalln(args...) +} + +func (logger *Logger) Panicln(args ...interface{}) { + NewEntry(logger).Panicln(args...) +} diff --git a/logrus.go b/logrus.go index 457343c..fdce43c 100644 --- a/logrus.go +++ b/logrus.go @@ -1,18 +1,6 @@ -package main +package logrus -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "os" - "strings" - "sync" - "time" - - "github.com/tobi/airbrake-go" -) +import () // TODO: Type naming here feels awkward, but the exposed variable should be // Level. That's more important than the type name, and libraries should be @@ -48,306 +36,3 @@ type StandardLogger interface { Panicf(string, ...interface{}) Panicln(...interface{}) } - -type Logger struct { - Out io.Writer - mu sync.Mutex -} - -type Entry struct { - logger *Logger - Data Fields -} - -// TODO: Other formats? -func (entry *Entry) Reader() (read *bytes.Buffer, err error) { - var serialized []byte - - if Environment == "production" { - serialized, err = json.Marshal(entry.Data) - } else { - // TODO: Pretty-print more by coloring when stdout is a tty - serialized, err = json.MarshalIndent(entry.Data, "", " ") - } - - if err != nil { - return nil, err - } - - serialized = append(serialized, '\n') - - return bytes.NewBuffer(serialized), nil -} - -func New() *Logger { - environment := strings.ToLower(os.Getenv("ENV")) - if environment == "" { - environment = "development" - } - - if airbrake.Environment == "" { - airbrake.Environment = environment - } - - return &Logger{ - Out: os.Stdout, // Default to stdout, change it if you want. - } -} - -func NewEntry(logger *Logger) *Entry { - return &Entry{ - logger: logger, - // Default is three fields, give a little extra room. Shouldn't hurt the - // scale. - Data: make(Fields, 5), - } -} - -func (logger *Logger) WithField(key string, value interface{}) *Entry { - entry := NewEntry(logger) - entry.WithField(key, value) - return entry -} - -func (logger *Logger) WithFields(fields Fields) *Entry { - entry := NewEntry(logger) - entry.WithFields(fields) - return entry -} - -func (entry *Entry) WithField(key string, value interface{}) *Entry { - entry.Data[key] = value - return entry -} - -func (entry *Entry) WithFields(fields Fields) *Entry { - for key, value := range fields { - entry.WithField(key, value) - } - - return entry -} - -func (entry *Entry) log(level string, msg string) { - // TODO: Is the default format output from String() the one we want? - entry.Data["timestamp"] = time.Now().String() - entry.Data["level"] = level - // TODO: Is this the best name? - entry.Data["msg"] = msg - - reader, err := entry.Reader() - if err != nil { - entry.logger.Panicln("Failed to marshal JSON ", err.Error()) - } - - // Send HTTP request in a goroutine in warning environment to not halt the - // main thread. It's sent before logging due to panic. - if level == "warning" { - // TODO: new() should spawn an airbrake goroutine and this should send to - // that channel. This prevent us from spawning hundreds of goroutines in a - // hot code path generating a warning. - go entry.airbrake(reader.String()) - } else if level == "fatal" || level == "panic" { - entry.airbrake(reader.String()) - } - - if level == "panic" { - panic(reader.String()) - } else { - entry.logger.mu.Lock() - io.Copy(entry.logger.Out, reader) - entry.logger.mu.Unlock() - } -} - -func (entry *Entry) airbrake(exception string) { - err := airbrake.Notify(errors.New(exception)) - if err != nil { - entry.logger.WithFields(Fields{ - "source": "airbrake", - "endpoint": airbrake.Endpoint, - }).Infof("Failed to send exception to Airbrake") - } -} - -// Entry Print family functions - -func (entry *Entry) Debug(args ...interface{}) { - if Level >= LevelDebug { - entry.log("debug", fmt.Sprint(args...)) - } -} - -func (entry *Entry) Info(args ...interface{}) { - if Level >= LevelInfo { - entry.log("info", fmt.Sprint(args...)) - } -} - -func (entry *Entry) Print(args ...interface{}) { - if Level >= LevelInfo { - entry.log("info", fmt.Sprint(args...)) - } -} - -func (entry *Entry) Warning(args ...interface{}) { - if Level >= LevelWarning { - entry.log("warning", fmt.Sprint(args...)) - } -} - -func (entry *Entry) Fatal(args ...interface{}) { - if Level >= LevelFatal { - entry.log("fatal", fmt.Sprint(args...)) - } - os.Exit(1) -} - -func (entry *Entry) Panic(args ...interface{}) { - if Level >= LevelPanic { - entry.log("panic", fmt.Sprint(args...)) - } -} - -// Entry Printf family functions - -func (entry *Entry) Debugf(format string, args ...interface{}) { - entry.Debug(fmt.Sprintf(format, args...)) -} - -func (entry *Entry) Infof(format string, args ...interface{}) { - entry.Info(fmt.Sprintf(format, args...)) -} - -func (entry *Entry) Printf(format string, args ...interface{}) { - entry.Print(fmt.Sprintf(format, args...)) -} - -func (entry *Entry) Warningf(format string, args ...interface{}) { - entry.Warning(fmt.Sprintf(format, args...)) -} - -func (entry *Entry) Fatalf(format string, args ...interface{}) { - entry.Fatal(fmt.Sprintf(format, args...)) -} - -func (entry *Entry) Panicf(format string, args ...interface{}) { - entry.Panic(fmt.Sprintf(format, args...)) -} - -// Entry Println family functions - -func (entry *Entry) Debugln(args ...interface{}) { - entry.Debug(fmt.Sprintln(args...)) -} - -func (entry *Entry) Infoln(args ...interface{}) { - entry.Info(fmt.Sprintln(args...)) -} - -func (entry *Entry) Println(args ...interface{}) { - entry.Print(fmt.Sprintln(args...)) -} - -func (entry *Entry) Warningln(args ...interface{}) { - entry.Warning(fmt.Sprintln(args...)) -} - -func (entry *Entry) Fatalln(args ...interface{}) { - entry.Fatal(fmt.Sprintln(args...)) -} - -func (entry *Entry) Panicln(args ...interface{}) { - entry.Panic(fmt.Sprintln(args...)) -} - -// Logger Printf family functions - -func (logger *Logger) Debugf(format string, args ...interface{}) { - NewEntry(logger).Debugf(format, args...) -} - -func (logger *Logger) Infof(format string, args ...interface{}) { - NewEntry(logger).Infof(format, args...) -} - -func (logger *Logger) Printf(format string, args ...interface{}) { - NewEntry(logger).Printf(format, args...) -} - -func (logger *Logger) Warningf(format string, args ...interface{}) { - NewEntry(logger).Warningf(format, args...) -} - -func (logger *Logger) Fatalf(format string, args ...interface{}) { - NewEntry(logger).Fatalf(format, args...) -} - -func (logger *Logger) Panicf(format string, args ...interface{}) { - NewEntry(logger).Panicf(format, args...) -} - -// Logger Print family functions - -func (logger *Logger) Debug(args ...interface{}) { - NewEntry(logger).Debug(args...) -} - -func (logger *Logger) Info(args ...interface{}) { - NewEntry(logger).Info(args...) -} - -func (logger *Logger) Print(args ...interface{}) { - NewEntry(logger).Print(args...) -} - -func (logger *Logger) Warning(args ...interface{}) { - NewEntry(logger).Warning(args...) -} - -func (logger *Logger) Fatal(args ...interface{}) { - NewEntry(logger).Fatal(args...) -} - -func (logger *Logger) Panic(args ...interface{}) { - NewEntry(logger).Panic(args...) -} - -// Logger Println family functions - -func (logger *Logger) Debugln(args ...interface{}) { - NewEntry(logger).Debugln(args...) -} - -func (logger *Logger) Infoln(args ...interface{}) { - NewEntry(logger).Infoln(args...) -} - -func (logger *Logger) Println(args ...interface{}) { - NewEntry(logger).Println(args...) -} - -func (logger *Logger) Warningln(args ...interface{}) { - NewEntry(logger).Warningln(args...) -} - -func (logger *Logger) Fatalln(args ...interface{}) { - NewEntry(logger).Fatalln(args...) -} - -func (logger *Logger) Panicln(args ...interface{}) { - NewEntry(logger).Panicln(args...) -} - -// TODO: Print, Fatal, etc. - -func main() { - Environment = "development" - Level = LevelDebug - logger := New() - logger.WithField("animal", "walrus").WithField("value", 10).Infof("OMG HELLO") - logger.Infof("lolsup") - logger.Debugf("why brackets?") - logger.Debug("lolsup") - logger.Fatalf("omg") -}