Merge pull request #92 from rasky/no-extra-quoting

Avoid extra quotes where not strictly necessary.
diff --git a/logrus_test.go b/logrus_test.go
index 348ad53..5302542 100644
--- a/logrus_test.go
+++ b/logrus_test.go
@@ -44,8 +44,12 @@
 		}
 		kvArr := strings.Split(kv, "=")
 		key := strings.TrimSpace(kvArr[0])
-		val, err := strconv.Unquote(kvArr[1])
-		assert.NoError(t, err)
+		val := kvArr[1]
+		if kvArr[1][0] == '"' {
+			var err error
+			val, err = strconv.Unquote(val)
+			assert.NoError(t, err)
+		}
 		fields[key] = val
 	}
 	assertions(fields)
diff --git a/text_formatter.go b/text_formatter.go
index fcc074f..78e7889 100644
--- a/text_formatter.go
+++ b/text_formatter.go
@@ -3,6 +3,7 @@
 import (
 	"bytes"
 	"fmt"
+	"regexp"
 	"sort"
 	"strings"
 	"time"
@@ -19,6 +20,7 @@
 var (
 	baseTimestamp time.Time
 	isTerminal    bool
+	noQuoteNeeded *regexp.Regexp
 )
 
 func init() {
@@ -90,10 +92,32 @@
 	}
 }
 
+func needsQuoting(text string) bool {
+	for _, ch := range text {
+		if !((ch >= 'a' && ch <= 'z') ||
+			(ch >= 'A' && ch <= 'Z') ||
+			(ch >= '0' && ch < '9') ||
+			ch == '-' || ch == '.') {
+			return false
+		}
+	}
+	return true
+}
+
 func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
 	switch value.(type) {
-	case string, error:
-		fmt.Fprintf(b, "%v=%q ", key, value)
+	case string:
+		if needsQuoting(value.(string)) {
+			fmt.Fprintf(b, "%v=%s ", key, value)
+		} else {
+			fmt.Fprintf(b, "%v=%q ", key, value)
+		}
+	case error:
+		if needsQuoting(value.(error).Error()) {
+			fmt.Fprintf(b, "%v=%s ", key, value)
+		} else {
+			fmt.Fprintf(b, "%v=%q ", key, value)
+		}
 	default:
 		fmt.Fprintf(b, "%v=%v ", key, value)
 	}
diff --git a/text_formatter_test.go b/text_formatter_test.go
new file mode 100644
index 0000000..f604f1b
--- /dev/null
+++ b/text_formatter_test.go
@@ -0,0 +1,33 @@
+package logrus
+
+import (
+	"bytes"
+	"errors"
+
+	"testing"
+)
+
+func TestQuoting(t *testing.T) {
+	tf := &TextFormatter{DisableColors: true}
+
+	checkQuoting := func(q bool, value interface{}) {
+		b, _ := tf.Format(WithField("test", value))
+		idx := bytes.Index(b, ([]byte)("test="))
+		cont := bytes.Contains(b[idx+5:], []byte{'"'})
+		if cont != q {
+			if q {
+				t.Errorf("quoting expected for: %#v", value)
+			} else {
+				t.Errorf("quoting not expected for: %#v", value)
+			}
+		}
+	}
+
+	checkQuoting(false, "abcd")
+	checkQuoting(false, "v1.0")
+	checkQuoting(true, "/foobar")
+	checkQuoting(true, "x y")
+	checkQuoting(true, "x,y")
+	checkQuoting(false, errors.New("invalid"))
+	checkQuoting(true, errors.New("invalid argument"))
+}