Merge pull request #396 from onetwopunch/customzable-json-keys

Added customizable keys to JSON formatter
diff --git a/json_formatter.go b/json_formatter.go
index 2ad6dc5..f3729bf 100644
--- a/json_formatter.go
+++ b/json_formatter.go
@@ -5,9 +5,37 @@
 	"fmt"
 )
 
+type fieldKey string
+type FieldMap map[fieldKey]string
+
+const (
+	FieldKeyMsg   = "msg"
+	FieldKeyLevel = "level"
+	FieldKeyTime  = "time"
+)
+
+func (f FieldMap) resolve(key fieldKey) string {
+	if k, ok := f[key]; ok {
+		return k
+	}
+
+	return string(key)
+}
+
 type JSONFormatter struct {
 	// TimestampFormat sets the format used for marshaling timestamps.
 	TimestampFormat string
+
+	// FieldMap allows users to customize the names of keys for various fields.
+	// As an example:
+	// formatter := &JSONFormatter{
+	//   	FieldMap: FieldMap{
+	// 		 FieldKeyTime: "@timestamp",
+	// 		 FieldKeyLevel: "@level",
+	// 		 FieldKeyLevel: "@message",
+	//    },
+	// }
+	FieldMap FieldMap
 }
 
 func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
@@ -29,9 +57,9 @@
 		timestampFormat = DefaultTimestampFormat
 	}
 
-	data["time"] = entry.Time.Format(timestampFormat)
-	data["msg"] = entry.Message
-	data["level"] = entry.Level.String()
+	data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
+	data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
+	data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
 
 	serialized, err := json.Marshal(data)
 	if err != nil {
diff --git a/json_formatter_test.go b/json_formatter_test.go
index 1d70873..5baa93e 100644
--- a/json_formatter_test.go
+++ b/json_formatter_test.go
@@ -3,7 +3,7 @@
 import (
 	"encoding/json"
 	"errors"
-
+	"strings"
 	"testing"
 )
 
@@ -118,3 +118,54 @@
 		t.Fatal("Expected JSON log entry to end with a newline")
 	}
 }
+
+func TestJSONMessageKey(t *testing.T) {
+	formatter := &JSONFormatter{
+		FieldMap: FieldMap{
+			FieldKeyMsg: "message",
+		},
+	}
+
+	b, err := formatter.Format(&Entry{Message: "oh hai"})
+	if err != nil {
+		t.Fatal("Unable to format entry: ", err)
+	}
+	s := string(b)
+	if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) {
+		t.Fatal("Expected JSON to format message key")
+	}
+}
+
+func TestJSONLevelKey(t *testing.T) {
+	formatter := &JSONFormatter{
+		FieldMap: FieldMap{
+			FieldKeyLevel: "somelevel",
+		},
+	}
+
+	b, err := formatter.Format(WithField("level", "something"))
+	if err != nil {
+		t.Fatal("Unable to format entry: ", err)
+	}
+	s := string(b)
+	if !strings.Contains(s, "somelevel") {
+		t.Fatal("Expected JSON to format level key")
+	}
+}
+
+func TestJSONTimeKey(t *testing.T) {
+	formatter := &JSONFormatter{
+		FieldMap: FieldMap{
+			FieldKeyTime: "timeywimey",
+		},
+	}
+
+	b, err := formatter.Format(WithField("level", "something"))
+	if err != nil {
+		t.Fatal("Unable to format entry: ", err)
+	}
+	s := string(b)
+	if !strings.Contains(s, "timeywimey") {
+		t.Fatal("Expected JSON to format time key")
+	}
+}