Naming, layout and comments: descriptive full-word names, one item per line in wrapped calls and literals, and doc comments on exported names
Short and single-letter names in application code become full words (tests keep theirs). Wrapped calls, signatures and composite literals put one item per line, and one-line function bodies are expanded. Exported names and groups of constants get doc comments, and comments refer to the renamed identifiers.
This commit is contained in:
1 parent
043e307663
commit
465358609a
78 files changed
+2374
-1402
No files matched your search
@@ -26,32 +26,32 @@ func (r *statusRecorder) WriteHeader(code int) {
|
||||
r.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (r *statusRecorder) Write(b []byte) (int, error) {
|
||||
func (r *statusRecorder) Write(data []byte) (int, error) {
|
||||
if r.status == 0 {
|
||||
r.status = http.StatusOK
|
||||
}
|
||||
n, err := r.ResponseWriter.Write(b)
|
||||
r.bytes += n
|
||||
return n, err
|
||||
written, err := r.ResponseWriter.Write(data)
|
||||
r.bytes += written
|
||||
return written, err
|
||||
}
|
||||
|
||||
// logField escapes a value for a quoted log field the way nginx does
|
||||
// (quotes, backslashes and control characters become \xHH) and uses "-"
|
||||
// for empty values.
|
||||
func logField(s string) string {
|
||||
if s == "" {
|
||||
func logField(value string) string {
|
||||
if value == "" {
|
||||
return "-"
|
||||
}
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c == '"' || c == '\\' || c < 0x20 || c == 0x7f {
|
||||
fmt.Fprintf(&b, "\\x%02X", c)
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(value); i++ {
|
||||
char := value[i]
|
||||
if char == '"' || char == '\\' || char < 0x20 || char == 0x7f {
|
||||
fmt.Fprintf(&builder, "\\x%02X", char)
|
||||
} else {
|
||||
b.WriteByte(c)
|
||||
builder.WriteByte(char)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// clientKey attempts to produce a client host name as a key identifier to split
|
||||
@@ -71,26 +71,29 @@ func clientKey(req *http.Request) string {
|
||||
func AccessLog(logger *log.Logger, combined bool, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
start := time.Now()
|
||||
rec := &statusRecorder{ResponseWriter: w}
|
||||
next.ServeHTTP(rec, req)
|
||||
if rec.status == 0 {
|
||||
rec.status = http.StatusOK
|
||||
recorder := &statusRecorder{
|
||||
ResponseWriter: w,
|
||||
}
|
||||
next.ServeHTTP(recorder, req)
|
||||
if recorder.status == 0 {
|
||||
recorder.status = http.StatusOK
|
||||
}
|
||||
|
||||
user := "-"
|
||||
if u, _, ok := req.BasicAuth(); ok && u != "" {
|
||||
user = logField(u)
|
||||
if username, _, ok := req.BasicAuth(); ok && username != "" {
|
||||
user = logField(username)
|
||||
}
|
||||
size := "-" // CLF uses "-" for an empty body
|
||||
if rec.bytes > 0 {
|
||||
size = strconv.Itoa(rec.bytes)
|
||||
if recorder.bytes > 0 {
|
||||
size = strconv.Itoa(recorder.bytes)
|
||||
}
|
||||
line := fmt.Sprintf(`%s - %s [%s] "%s" %d %s`,
|
||||
line := fmt.Sprintf(
|
||||
`%s - %s [%s] "%s" %d %s`,
|
||||
clientKey(req),
|
||||
user,
|
||||
start.Format("02/Jan/2006:15:04:05 -0700"),
|
||||
logField(req.Method+" "+req.RequestURI+" "+req.Proto),
|
||||
rec.status,
|
||||
recorder.status,
|
||||
size,
|
||||
)
|
||||
if combined {
|
||||
|
||||
@@ -40,7 +40,9 @@ func TestClientKey(t *testing.T) {
|
||||
{"", ""},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
req := &http.Request{RemoteAddr: tc.remote}
|
||||
req := &http.Request{
|
||||
RemoteAddr: tc.remote,
|
||||
}
|
||||
if got := clientKey(req); got != tc.want {
|
||||
t.Errorf("clientKey(%q) = %q, want %q", tc.remote, got, tc.want)
|
||||
}
|
||||
@@ -49,7 +51,9 @@ func TestClientKey(t *testing.T) {
|
||||
|
||||
func TestStatusRecorder(t *testing.T) {
|
||||
t.Run("first WriteHeader wins", func(t *testing.T) {
|
||||
rec := &statusRecorder{ResponseWriter: httptest.NewRecorder()}
|
||||
rec := &statusRecorder{
|
||||
ResponseWriter: httptest.NewRecorder(),
|
||||
}
|
||||
rec.WriteHeader(http.StatusTeapot)
|
||||
rec.WriteHeader(http.StatusInternalServerError)
|
||||
if rec.status != http.StatusTeapot {
|
||||
@@ -58,7 +62,9 @@ func TestStatusRecorder(t *testing.T) {
|
||||
})
|
||||
t.Run("Write implies 200 and counts bytes", func(t *testing.T) {
|
||||
inner := httptest.NewRecorder()
|
||||
rec := &statusRecorder{ResponseWriter: inner}
|
||||
rec := &statusRecorder{
|
||||
ResponseWriter: inner,
|
||||
}
|
||||
rec.Write([]byte("hello"))
|
||||
rec.Write([]byte(" world"))
|
||||
if rec.status != http.StatusOK || rec.bytes != 11 {
|
||||
@@ -81,9 +87,10 @@ func TestAccessLog(t *testing.T) {
|
||||
want string // regexp for the whole line
|
||||
}{
|
||||
{
|
||||
name: "no body defaults to 200 and -",
|
||||
handler: func(http.ResponseWriter, *http.Request) {},
|
||||
want: `^192\.0\.2\.1 - - ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 200 -$`,
|
||||
name: "no body defaults to 200 and -",
|
||||
handler: func(http.ResponseWriter, *http.Request) {
|
||||
},
|
||||
want: `^192\.0\.2\.1 - - ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 200 -$`,
|
||||
},
|
||||
{
|
||||
name: "status and size",
|
||||
@@ -94,15 +101,20 @@ func TestAccessLog(t *testing.T) {
|
||||
want: `^192\.0\.2\.1 - - ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 404 4$`,
|
||||
},
|
||||
{
|
||||
name: "basic auth user is escaped",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte("x")) },
|
||||
setup: func(r *http.Request) { r.SetBasicAuth(`bo"b`, "pw") },
|
||||
want: `^192\.0\.2\.1 - bo\\x22b ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 200 1$`,
|
||||
name: "basic auth user is escaped",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte("x"))
|
||||
},
|
||||
setup: func(r *http.Request) {
|
||||
r.SetBasicAuth(`bo"b`, "pw")
|
||||
},
|
||||
want: `^192\.0\.2\.1 - bo\\x22b ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 200 1$`,
|
||||
},
|
||||
{
|
||||
name: "combined adds referer and user agent",
|
||||
combined: true,
|
||||
handler: func(http.ResponseWriter, *http.Request) {},
|
||||
handler: func(http.ResponseWriter, *http.Request) {
|
||||
},
|
||||
setup: func(r *http.Request) {
|
||||
r.Header.Set("Referer", "http://ref/")
|
||||
r.Header.Set("User-Agent", `ua "1"`)
|
||||
@@ -112,9 +124,12 @@ func TestAccessLog(t *testing.T) {
|
||||
{
|
||||
name: "combined with missing headers",
|
||||
combined: true,
|
||||
handler: func(http.ResponseWriter, *http.Request) {},
|
||||
setup: func(r *http.Request) { r.Header.Del("User-Agent") },
|
||||
want: `^192\.0\.2\.1 - - ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 200 - "-" "-"$`,
|
||||
handler: func(http.ResponseWriter, *http.Request) {
|
||||
},
|
||||
setup: func(r *http.Request) {
|
||||
r.Header.Del("User-Agent")
|
||||
},
|
||||
want: `^192\.0\.2\.1 - - ` + clfTime + ` "GET /path\?q=1 HTTP/1\.1" 200 - "-" "-"$`,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
|
||||
@@ -135,9 +135,9 @@ func (h terminalHandler) WithGroup(name string) slog.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// IsJSONHandler reports whether h writes JSON, as the file logger does.
|
||||
func IsJSONHandler(h slog.Handler) bool {
|
||||
_, ok := h.(*slog.JSONHandler)
|
||||
// IsJSONHandler reports whether handler writes JSON, as the file logger does.
|
||||
func IsJSONHandler(handler slog.Handler) bool {
|
||||
_, ok := handler.(*slog.JSONHandler)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
@@ -67,9 +67,12 @@ func TestIsJSONHandler(t *testing.T) {
|
||||
{"tint", tint.NewTextHandler(io.Discard, nil), false},
|
||||
{"nil", nil, false},
|
||||
{"json with attrs", slog.NewJSONHandler(io.Discard, nil).WithAttrs(
|
||||
[]slog.Attr{slog.String("k", "v")}), true},
|
||||
[]slog.Attr{slog.String("k", "v")},
|
||||
), true},
|
||||
// Only slog's own JSON handler is recognised, not one wrapped in another.
|
||||
{"wrapped json", wrapper{slog.NewJSONHandler(io.Discard, nil)}, false},
|
||||
{"wrapped json", wrapper{
|
||||
Handler: slog.NewJSONHandler(io.Discard, nil),
|
||||
}, false},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user