diff --git a/text_formatter.go b/text_formatter.go index 8006c0d..32adbf8 100644 --- a/text_formatter.go +++ b/text_formatter.go @@ -174,14 +174,20 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { if !f.needsQuoting(value) { b.WriteString(value) } else { - fmt.Fprintf(b, "%q", value) + escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) + escapedValue := strings.Replace(value, f.QuoteCharacter, escapedQuote, -1) + + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, escapedValue, f.QuoteCharacter) } case error: errmsg := value.Error() if !f.needsQuoting(errmsg) { b.WriteString(errmsg) } else { - fmt.Fprintf(b, "%q", errmsg) + escapedQuote := fmt.Sprintf("\\%s", f.QuoteCharacter) + escapedErrmsg := strings.Replace(errmsg, f.QuoteCharacter, escapedQuote, -1) + + fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, escapedErrmsg, f.QuoteCharacter) } default: fmt.Fprint(b, value) diff --git a/text_formatter_test.go b/text_formatter_test.go index 9793b5f..f02dc0c 100644 --- a/text_formatter_test.go +++ b/text_formatter_test.go @@ -53,6 +53,48 @@ func TestQuoting(t *testing.T) { checkQuoting(true, errors.New("invalid argument")) } +func TestEscaping_DefaultQuoteCharacter(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + }{ + {`ba"r`, `ba\"r`}, + {`ba'r`, `ba'r`}, + } + + for _, tc := range testCases { + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + +func TestEscaping_CustomQuoteCharacter(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + testCases := []struct { + value string + expected string + quoteChar string + }{ + {`ba"r`, `ba"r`, `'`}, + {`ba'r`, `ba\'r`, `'`}, + {`ba^r`, `ba\^r`, `^`}, + {`ba'r`, `ba'r`, `^`}, + } + + for _, tc := range testCases { + tf.QuoteCharacter = tc.quoteChar + b, _ := tf.Format(WithField("test", tc.value)) + if !bytes.Contains(b, []byte(tc.expected)) { + t.Errorf("escaping expected for %q (result was %q instead of %q)", tc.value, string(b), tc.expected) + } + } +} + func TestTimestampFormat(t *testing.T) { checkTimeStr := func(format string) { customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}