Skip to content

Commit e5a6874

Browse files
author
Evan Phoenix
authored
Merge pull request #99 from hashicorp/bug/quotes
Adjust the rules for deciding to quote a string value
2 parents 480ace9 + 9b7c3bf commit e5a6874

File tree

3 files changed

+113
-47
lines changed

3 files changed

+113
-47
lines changed

intlogger.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,24 @@ func trimCallerPath(path string) string {
199199
return path[idx+1:]
200200
}
201201

202+
// isNormal indicates if the rune is one allowed to exist as an unquoted
203+
// string value. This is a subset of ASCII, `-` through `~`.
204+
func isNormal(r rune) bool {
205+
return 0x2D <= r && r <= 0x7E // - through ~
206+
}
207+
208+
// needsQuoting returns false if all the runes in string are normal, according
209+
// to isNormal
210+
func needsQuoting(str string) bool {
211+
for _, r := range str {
212+
if !isNormal(r) {
213+
return true
214+
}
215+
}
216+
217+
return false
218+
}
219+
202220
// Non-JSON logging format function
203221
func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string, args ...interface{}) {
204222

@@ -323,13 +341,11 @@ func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string,
323341
l.writer.WriteString("=\n")
324342
writeIndent(l.writer, val, " | ")
325343
l.writer.WriteString(" ")
326-
} else if !raw && strings.ContainsAny(val, " \t") {
344+
} else if !raw && needsQuoting(val) {
327345
l.writer.WriteByte(' ')
328346
l.writer.WriteString(key)
329347
l.writer.WriteByte('=')
330-
l.writer.WriteByte('"')
331-
l.writer.WriteString(val)
332-
l.writer.WriteByte('"')
348+
l.writer.WriteString(strconv.Quote(val))
333349
} else {
334350
l.writer.WriteByte(' ')
335351
l.writer.WriteString(key)

logger_loc_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package hclog
2+
3+
import (
4+
"bytes"
5+
"strings"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
// This file contains tests that are sensitive to their location in the file,
12+
// because they contain line numbers. They're basically "quarantined" from the
13+
// other tests because they break all the time when new tests are added.
14+
15+
func TestLoggerLoc(t *testing.T) {
16+
t.Run("includes the caller location", func(t *testing.T) {
17+
var buf bytes.Buffer
18+
19+
logger := New(&LoggerOptions{
20+
Name: "test",
21+
Output: &buf,
22+
IncludeLocation: true,
23+
})
24+
25+
logger.Info("this is test", "who", "programmer", "why", "testing is fun")
26+
27+
str := buf.String()
28+
dataIdx := strings.IndexByte(str, ' ')
29+
rest := str[dataIdx+1:]
30+
31+
// This test will break if you move this around, it's line dependent, just fyi
32+
assert.Equal(t, "[INFO] go-hclog/logger_loc_test.go:25: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
33+
})
34+
35+
t.Run("includes the caller location excluding helper functions", func(t *testing.T) {
36+
var buf bytes.Buffer
37+
38+
logMe := func(l Logger) {
39+
l.Info("this is test", "who", "programmer", "why", "testing is fun")
40+
}
41+
42+
logger := New(&LoggerOptions{
43+
Name: "test",
44+
Output: &buf,
45+
IncludeLocation: true,
46+
AdditionalLocationOffset: 1,
47+
})
48+
49+
logMe(logger)
50+
51+
str := buf.String()
52+
dataIdx := strings.IndexByte(str, ' ')
53+
rest := str[dataIdx+1:]
54+
55+
// This test will break if you move this around, it's line dependent, just fyi
56+
assert.Equal(t, "[INFO] go-hclog/logger_loc_test.go:49: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
57+
})
58+
59+
}

logger_test.go

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -103,101 +103,92 @@ func TestLogger(t *testing.T) {
103103
assert.Equal(t, "[INFO] test: this is test: who=programmer why=[\"testing & qa\", \"dev\"]\n", rest)
104104
})
105105

106-
t.Run("formats multiline values nicely", func(t *testing.T) {
106+
t.Run("escapes quotes in values", func(t *testing.T) {
107107
var buf bytes.Buffer
108108

109109
logger := New(&LoggerOptions{
110110
Name: "test",
111111
Output: &buf,
112112
})
113113

114-
logger.Info("this is test", "who", "programmer", "why", "testing\nand other\npretty cool things")
114+
logger.Info("this is test", "who", "programmer", "why", `this is "quoted"`)
115115

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

120-
expected := `[INFO] test: this is test: who=programmer
121-
why=
122-
| testing
123-
| and other
124-
| pretty cool things` + "\n \n"
125-
assert.Equal(t, expected, rest)
120+
assert.Equal(t, `[INFO] test: this is test: who=programmer why="this is \"quoted\""`+"\n", rest)
126121
})
127122

128-
t.Run("outputs stack traces", func(t *testing.T) {
123+
t.Run("quotes when there are nonprintable sequences in a value", func(t *testing.T) {
129124
var buf bytes.Buffer
130125

131126
logger := New(&LoggerOptions{
132127
Name: "test",
133128
Output: &buf,
134129
})
135130

136-
logger.Info("who", "programmer", "why", "testing", Stacktrace())
131+
logger.Info("this is test", "who", "programmer", "why", "\U0001F603")
137132

138-
lines := strings.Split(buf.String(), "\n")
139-
require.True(t, len(lines) > 1)
133+
str := buf.String()
134+
dataIdx := strings.IndexByte(str, ' ')
135+
rest := str[dataIdx+1:]
140136

141-
assert.Equal(t, "github.com/hashicorp/go-hclog.Stacktrace", lines[1])
137+
assert.Equal(t, "[INFO] test: this is test: who=programmer why=\"\U0001F603\"\n", rest)
142138
})
143139

144-
t.Run("outputs stack traces with it's given a name", func(t *testing.T) {
140+
t.Run("formats multiline values nicely", func(t *testing.T) {
145141
var buf bytes.Buffer
146142

147143
logger := New(&LoggerOptions{
148144
Name: "test",
149145
Output: &buf,
150146
})
151147

152-
logger.Info("who", "programmer", "why", "testing", "foo", Stacktrace())
148+
logger.Info("this is test", "who", "programmer", "why", "testing\nand other\npretty cool things")
153149

154-
lines := strings.Split(buf.String(), "\n")
155-
require.True(t, len(lines) > 1)
150+
str := buf.String()
151+
dataIdx := strings.IndexByte(str, ' ')
152+
rest := str[dataIdx+1:]
156153

157-
assert.Equal(t, "github.com/hashicorp/go-hclog.Stacktrace", lines[1])
154+
expected := `[INFO] test: this is test: who=programmer
155+
why=
156+
| testing
157+
| and other
158+
| pretty cool things` + "\n \n"
159+
assert.Equal(t, expected, rest)
158160
})
159161

160-
t.Run("includes the caller location", func(t *testing.T) {
162+
t.Run("outputs stack traces", func(t *testing.T) {
161163
var buf bytes.Buffer
162164

163165
logger := New(&LoggerOptions{
164-
Name: "test",
165-
Output: &buf,
166-
IncludeLocation: true,
166+
Name: "test",
167+
Output: &buf,
167168
})
168169

169-
logger.Info("this is test", "who", "programmer", "why", "testing is fun")
170+
logger.Info("who", "programmer", "why", "testing", Stacktrace())
170171

171-
str := buf.String()
172-
dataIdx := strings.IndexByte(str, ' ')
173-
rest := str[dataIdx+1:]
172+
lines := strings.Split(buf.String(), "\n")
173+
require.True(t, len(lines) > 1)
174174

175-
// This test will break if you move this around, it's line dependent, just fyi
176-
assert.Equal(t, "[INFO] go-hclog/logger_test.go:169: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
175+
assert.Equal(t, "github.com/hashicorp/go-hclog.Stacktrace", lines[1])
177176
})
178177

179-
t.Run("includes the caller location excluding helper functions", func(t *testing.T) {
178+
t.Run("outputs stack traces with it's given a name", func(t *testing.T) {
180179
var buf bytes.Buffer
181180

182-
logMe := func(l Logger) {
183-
l.Info("this is test", "who", "programmer", "why", "testing is fun")
184-
}
185-
186181
logger := New(&LoggerOptions{
187-
Name: "test",
188-
Output: &buf,
189-
IncludeLocation: true,
190-
AdditionalLocationOffset: 1,
182+
Name: "test",
183+
Output: &buf,
191184
})
192185

193-
logMe(logger)
186+
logger.Info("who", "programmer", "why", "testing", "foo", Stacktrace())
194187

195-
str := buf.String()
196-
dataIdx := strings.IndexByte(str, ' ')
197-
rest := str[dataIdx+1:]
188+
lines := strings.Split(buf.String(), "\n")
189+
require.True(t, len(lines) > 1)
198190

199-
// This test will break if you move this around, it's line dependent, just fyi
200-
assert.Equal(t, "[INFO] go-hclog/logger_test.go:193: test: this is test: who=programmer why=\"testing is fun\"\n", rest)
191+
assert.Equal(t, "github.com/hashicorp/go-hclog.Stacktrace", lines[1])
201192
})
202193

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

0 commit comments

Comments
 (0)