Skip to content

Commit

Permalink
Merge pull request #87 from hashicorp/b-multiline-improve
Browse files Browse the repository at this point in the history
Improve rendering of empty strings and multiline values
  • Loading branch information
evanphx authored Apr 5, 2021
2 parents c2b3341 + c102b1c commit a0601e6
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 17 deletions.
2 changes: 1 addition & 1 deletion interceptlogger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func TestInterceptLogger(t *testing.T) {
output := buf.String()
dataIdx := strings.IndexByte(output, ' ')
rest := output[dataIdx+1:]
assert.Equal(t, "[DEBUG] with_test.sub_logger.http: test1: parent=logger path=/some/test/path args=[test, test]\n", rest)
assert.Equal(t, "[DEBUG] with_test.sub_logger.http: test1: parent=logger path=/some/test/path args=[\"test\", \"test\"]\n", rest)
})

t.Run("derived standard loggers send output to sinks", func(t *testing.T) {
Expand Down
57 changes: 44 additions & 13 deletions intlogger.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string,
switch st := args[i+1].(type) {
case string:
val = st
if st == "" {
val = `""`
}
case int:
val = strconv.FormatInt(int64(st), 10)
case int64:
Expand Down Expand Up @@ -302,20 +305,32 @@ func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string,
}
}

l.writer.WriteByte(' ')
var key string

switch st := args[i].(type) {
case string:
l.writer.WriteString(st)
key = st
default:
l.writer.WriteString(fmt.Sprintf("%s", st))
key = fmt.Sprintf("%s", st)
}
l.writer.WriteByte('=')

if !raw && strings.ContainsAny(val, " \t\n\r") {
if strings.Contains(val, "\n") {
l.writer.WriteString("\n ")
l.writer.WriteString(key)
l.writer.WriteString("=\n")
writeIndent(l.writer, val, " | ")
l.writer.WriteString(" ")
} else if !raw && strings.ContainsAny(val, " \t") {
l.writer.WriteByte(' ')
l.writer.WriteString(key)
l.writer.WriteByte('=')
l.writer.WriteByte('"')
l.writer.WriteString(val)
l.writer.WriteByte('"')
} else {
l.writer.WriteByte(' ')
l.writer.WriteString(key)
l.writer.WriteByte('=')
l.writer.WriteString(val)
}
}
Expand All @@ -329,6 +344,25 @@ func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string,
}
}

func writeIndent(w *writer, str string, indent string) {
for {
nl := strings.IndexByte(str, "\n"[0])
if nl == -1 {
if str != "" {
w.WriteString(indent)
w.WriteString(str)
w.WriteString("\n")
}
return
}

w.WriteString(indent)
w.WriteString(str[:nl])
w.WriteString("\n")
str = str[nl+1:]
}
}

func (l *intLogger) renderSlice(v reflect.Value) string {
var buf bytes.Buffer

Expand All @@ -345,22 +379,19 @@ func (l *intLogger) renderSlice(v reflect.Value) string {

switch sv.Kind() {
case reflect.String:
val = sv.String()
val = strconv.Quote(sv.String())
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
val = strconv.FormatInt(sv.Int(), 10)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val = strconv.FormatUint(sv.Uint(), 10)
default:
val = fmt.Sprintf("%v", sv.Interface())
if strings.ContainsAny(val, " \t\n\r") {
val = strconv.Quote(val)
}
}

if strings.ContainsAny(val, " \t\n\r") {
buf.WriteByte('"')
buf.WriteString(val)
buf.WriteByte('"')
} else {
buf.WriteString(val)
}
buf.WriteString(val)
}

buf.WriteRune(']')
Expand Down
28 changes: 25 additions & 3 deletions logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestLogger(t *testing.T) {
assert.Equal(t, "[INFO] test: this is test: who=programmer why=[testing, dev, 1, 5, \"[3 4]\"]\n", rest)
})

t.Run("renders values in slices with quotes if needed", func(t *testing.T) {
t.Run("renders values in slices with quotes", func(t *testing.T) {
var buf bytes.Buffer

logger := New(&LoggerOptions{
Expand All @@ -100,7 +100,29 @@ func TestLogger(t *testing.T) {
dataIdx := strings.IndexByte(str, ' ')
rest := str[dataIdx+1:]

assert.Equal(t, "[INFO] test: this is test: who=programmer why=[\"testing & qa\", dev]\n", rest)
assert.Equal(t, "[INFO] test: this is test: who=programmer why=[\"testing & qa\", \"dev\"]\n", rest)
})

t.Run("formats multiline values nicely", func(t *testing.T) {
var buf bytes.Buffer

logger := New(&LoggerOptions{
Name: "test",
Output: &buf,
})

logger.Info("this is test", "who", "programmer", "why", "testing\nand other\npretty cool things")

str := buf.String()
dataIdx := strings.IndexByte(str, ' ')
rest := str[dataIdx+1:]

expected := `[INFO] test: this is test: who=programmer
why=
| testing
| and other
| pretty cool things` + "\n \n"
assert.Equal(t, expected, rest)
})

t.Run("outputs stack traces", func(t *testing.T) {
Expand Down Expand Up @@ -151,7 +173,7 @@ func TestLogger(t *testing.T) {
rest := str[dataIdx+1:]

// This test will break if you move this around, it's line dependent, just fyi
assert.Equal(t, "[INFO] go-hclog/logger_test.go:147: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
assert.Equal(t, "[INFO] go-hclog/logger_test.go:169: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
})

t.Run("prefixes the name", func(t *testing.T) {
Expand Down

0 comments on commit a0601e6

Please sign in to comment.