Merge pull request #145 from alphagov/add_tests_to_airbrake_hook
Add integration test to Airbrake hook
diff --git a/examples/basic/basic.go b/examples/basic/basic.go
index a62ba45..a1623ec 100644
--- a/examples/basic/basic.go
+++ b/examples/basic/basic.go
@@ -9,6 +9,7 @@
func init() {
log.Formatter = new(logrus.JSONFormatter)
log.Formatter = new(logrus.TextFormatter) // default
+ log.Level = logrus.DebugLevel
}
func main() {
@@ -25,6 +26,11 @@
log.WithFields(logrus.Fields{
"animal": "walrus",
+ "number": 8,
+ }).Debug("Started observing beach")
+
+ log.WithFields(logrus.Fields{
+ "animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
@@ -34,6 +40,10 @@
}).Warn("The group's number increased tremendously!")
log.WithFields(logrus.Fields{
+ "temperature": -4,
+ }).Debug("Temperature changes")
+
+ log.WithFields(logrus.Fields{
"animal": "orca",
"size": 9009,
}).Panic("It's over 9000!")
diff --git a/hooks/airbrake/airbrake.go b/hooks/airbrake/airbrake.go
index 880d21e..75f4db1 100644
--- a/hooks/airbrake/airbrake.go
+++ b/hooks/airbrake/airbrake.go
@@ -9,7 +9,7 @@
// with the Airbrake API. You must set:
// * airbrake.Endpoint
// * airbrake.ApiKey
-// * airbrake.Environment (only sends exceptions when set to "production")
+// * airbrake.Environment
//
// Before using this hook, to send an error. Entries that trigger an Error,
// Fatal or Panic should now include an "error" field to send to Airbrake.
diff --git a/json_formatter.go b/json_formatter.go
index b09227c..0e38a61 100644
--- a/json_formatter.go
+++ b/json_formatter.go
@@ -11,7 +11,13 @@
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
- data[k] = v
+ // Otherwise errors are ignored by `encoding/json`
+ // https://github.com/Sirupsen/logrus/issues/137
+ if err, ok := v.(error); ok {
+ data[k] = err.Error()
+ } else {
+ data[k] = v
+ }
}
prefixFieldClashes(data)
data["time"] = entry.Time.Format(time.RFC3339)
diff --git a/json_formatter_test.go b/json_formatter_test.go
new file mode 100644
index 0000000..1d70873
--- /dev/null
+++ b/json_formatter_test.go
@@ -0,0 +1,120 @@
+package logrus
+
+import (
+ "encoding/json"
+ "errors"
+
+ "testing"
+)
+
+func TestErrorNotLost(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["error"] != "wild walrus" {
+ t.Fatal("Error field not set")
+ }
+}
+
+func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["omg"] != "wild walrus" {
+ t.Fatal("Error field not set")
+ }
+}
+
+func TestFieldClashWithTime(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("time", "right now!"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.time"] != "right now!" {
+ t.Fatal("fields.time not set to original time field")
+ }
+
+ if entry["time"] != "0001-01-01T00:00:00Z" {
+ t.Fatal("time field not set to current time, was: ", entry["time"])
+ }
+}
+
+func TestFieldClashWithMsg(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("msg", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.msg"] != "something" {
+ t.Fatal("fields.msg not set to original msg field")
+ }
+}
+
+func TestFieldClashWithLevel(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ entry := make(map[string]interface{})
+ err = json.Unmarshal(b, &entry)
+ if err != nil {
+ t.Fatal("Unable to unmarshal formatted entry: ", err)
+ }
+
+ if entry["fields.level"] != "something" {
+ t.Fatal("fields.level not set to original level field")
+ }
+}
+
+func TestJSONEntryEndsWithNewline(t *testing.T) {
+ formatter := &JSONFormatter{}
+
+ b, err := formatter.Format(WithField("level", "something"))
+ if err != nil {
+ t.Fatal("Unable to format entry: ", err)
+ }
+
+ if b[len(b)-1] != '\n' {
+ t.Fatal("Expected JSON log entry to end with a newline")
+ }
+}
diff --git a/text_formatter.go b/text_formatter.go
index e44f1e1..71dcb66 100644
--- a/text_formatter.go
+++ b/text_formatter.go
@@ -15,6 +15,7 @@
green = 32
yellow = 33
blue = 34
+ gray = 37
)
var (
@@ -46,15 +47,22 @@
// Enable logging the full timestamp when a TTY is attached instead of just
// the time passed since beginning of execution.
FullTimestamp bool
+
+ // The fields are sorted by default for a consistent output. For applications
+ // that log extremely frequently and don't use the JSON formatter this may not
+ // be desired.
+ DisableSorting bool
}
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
-
var keys []string = make([]string, 0, len(entry.Data))
for k := range entry.Data {
keys = append(keys, k)
}
- sort.Strings(keys)
+
+ if !f.DisableSorting {
+ sort.Strings(keys)
+ }
b := &bytes.Buffer{}
@@ -82,6 +90,8 @@
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string) {
var levelColor int
switch entry.Level {
+ case DebugLevel:
+ levelColor = gray
case WarnLevel:
levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel:
diff --git a/text_formatter_test.go b/text_formatter_test.go
index 396bc5f..28a9499 100644
--- a/text_formatter_test.go
+++ b/text_formatter_test.go
@@ -32,3 +32,6 @@
checkQuoting(false, errors.New("invalid"))
checkQuoting(true, errors.New("invalid argument"))
}
+
+// TODO add tests for sorting etc., this requires a parser for the text
+// formatter output.