Repository: hashicorp/go-hclog (go)

Issue:
Please provider a option to determine whether do escape or not when log in json format
Currently, when `intLogger` log in json format. It will call `json.NewEncoder` and do Encode.

But, the `escapeHTML` in `json.Encoder` is default to true. So when I want to log some info which will refer these symbol: &, <, and >, the log result is unexpected.

I would like to submit a PR to enhance it.

Pull request description:
support configure json escape when log in json format
To resolve #140 

This modification will not lead to some breaking change, because the attribute which added will default to false. If user want to use this feat, this attribute should be set to true.

Patch:
diff --git a/intlogger.go b/intlogger.go
index 104d82f..272a710 100644
--- a/intlogger.go
+++ b/intlogger.go
@@ -80,12 +80,13 @@ var _ Logger = &intLogger{}
 // intLogger is an internal logger implementation. Internal in that it is
 // defined entirely by this package.
 type intLogger struct {
-	json         bool
-	callerOffset int
-	name         string
-	timeFormat   string
-	timeFn       TimeFunction
-	disableTime  bool
+	json              bool
+	jsonEscapeEnabled bool
+	callerOffset      int
+	name              string
+	timeFormat        string
+	timeFn            TimeFunction
+	disableTime       bool
 
 	// This is an interface so that it's shared by any derived loggers, since
 	// those derived loggers share the bufio.Writer as well.
@@ -173,6 +174,7 @@ func newLogger(opts *LoggerOptions) *intLogger {
 
 	l := &intLogger{
 		json:              opts.JSONFormat,
+		jsonEscapeEnabled: !opts.JSONEscapeDisabled,
 		name:              opts.Name,
 		timeFormat:        TimeFormat,
 		timeFn:            time.Now,
@@ -667,13 +669,17 @@ func (l *intLogger) logJSON(t time.Time, name string, level Level, msg string, a
 		}
 	}
 
-	err := json.NewEncoder(l.writer).Encode(vals)
+	encoder := json.NewEncoder(l.writer)
+	encoder.SetEscapeHTML(l.jsonEscapeEnabled)
+	err := encoder.Encode(vals)
 	if err != nil {
 		if _, ok := err.(*json.UnsupportedTypeError); ok {
 			plainVal := l.jsonMapEntry(t, name, level, msg)
 			plainVal["@warn"] = errJsonUnsupportedTypeMsg
 
-			json.NewEncoder(l.writer).Encode(plainVal)
+			errEncoder := json.NewEncoder(l.writer)
+			errEncoder.SetEscapeHTML(l.jsonEscapeEnabled)
+			errEncoder.Encode(plainVal)
 		}
 	}
 }
diff --git a/logger.go b/logger.go
index d7806fb..ad17544 100644
--- a/logger.go
+++ b/logger.go
@@ -264,6 +264,9 @@ type LoggerOptions struct {
 	// Control if the output should be in JSON.
 	JSONFormat bool
 
+	// Control the escape switch of json.Encoder
+	JSONEscapeDisabled bool
+
 	// Include file and line information in each log line
 	IncludeLocation bool
 


Test patch:
diff --git a/logger_test.go b/logger_test.go
index dea7013..b24f059 100644
--- a/logger_test.go
+++ b/logger_test.go
@@ -1232,6 +1232,28 @@ func TestLogger_JSON(t *testing.T) {
 		assert.Equal(t, "[INFO]  test: who=programmer why=testing\n", rest)
 	})
 
+	t.Run("disable json escape when log special character", func(t *testing.T) {
+		var buf bytes.Buffer
+
+		logger := New(&LoggerOptions{
+			Name:               "test",
+			Output:             &buf,
+			JSONFormat:         true,
+			JSONEscapeDisabled: true,
+		})
+
+		logger.Info("this is test and use > < &")
+
+		b := buf.Bytes()
+
+		var raw map[string]interface{}
+		if err := json.Unmarshal(b, &raw); err != nil {
+			t.Fatal(err)
+		}
+
+		assert.Equal(t, "this is test and use > < &", raw["@message"])
+	})
+
 }
 
 type customErrJSON struct {