Added access log to server and modified mock-fts to use it
This commit is contained in:
1 parent
2490519c2b
commit
02e3d910a2
10 files changed
+389
-29
No files matched your search
@@ -120,9 +120,11 @@ func isRequestIDChar(char byte) bool {
|
||||
return char == '-' || char == '_' || char == '.'
|
||||
}
|
||||
|
||||
// Logger logs one line per request. The query string is deliberately not
|
||||
// logged, as it may carry secrets. It logs to slog.Default() unless given
|
||||
// WithLogger.
|
||||
// Logger logs one line per request: at debug level, or at info level for a
|
||||
// connection hijack (a WebSocket upgrade), as one is logged per connection
|
||||
// rather than per request; a 4xx response is logged at warn level and a 5xx
|
||||
// at error level. The query string is deliberately not logged, as it may
|
||||
// carry secrets. It logs to slog.Default() unless given WithLogger.
|
||||
func Logger(opts ...Option) func(http.Handler) http.Handler {
|
||||
settings := applyOptions(opts)
|
||||
return func(next http.Handler) http.Handler {
|
||||
@@ -138,12 +140,14 @@ func Logger(opts ...Option) func(http.Handler) http.Handler {
|
||||
status = http.StatusOK
|
||||
}
|
||||
|
||||
level := slog.LevelInfo
|
||||
level := slog.LevelDebug
|
||||
switch {
|
||||
case status >= 500:
|
||||
level = slog.LevelError
|
||||
case status >= 400:
|
||||
level = slog.LevelWarn
|
||||
case recorder.hijacked:
|
||||
level = slog.LevelInfo
|
||||
}
|
||||
|
||||
settings.logger("http").LogAttrs(
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// lockedBuffer is a bytes.Buffer safe to write from the server's goroutine
|
||||
// while the test reads it.
|
||||
type lockedBuffer struct {
|
||||
mu sync.Mutex
|
||||
buffer bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *lockedBuffer) Write(data []byte) (int, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.buffer.Write(data)
|
||||
}
|
||||
|
||||
func (b *lockedBuffer) String() string {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.buffer.String()
|
||||
}
|
||||
|
||||
// A request is logged at debug level, a hijacked connection at info, a 4xx
|
||||
// response at warn and a 5xx at error.
|
||||
func TestLoggerLevels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
want string
|
||||
}{
|
||||
{"ok", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}, "DEBUG"},
|
||||
{"not found", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}, "WARN"},
|
||||
{"server error", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}, "ERROR"},
|
||||
{"hijacked", func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, readWriter, err := http.NewResponseController(w).Hijack()
|
||||
if err != nil {
|
||||
t.Errorf("hijack: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
readWriter.WriteString("HTTP/1.1 204 No Content\r\n\r\n")
|
||||
readWriter.Flush()
|
||||
}, "INFO"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buffer lockedBuffer
|
||||
logger := slog.New(slog.NewJSONHandler(&buffer, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
server := httptest.NewServer(
|
||||
Logger(WithLogger(logger))(tt.handler),
|
||||
)
|
||||
defer server.Close()
|
||||
|
||||
res, err := http.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
// The line is written when the handler returns, which may be after
|
||||
// the client has its response.
|
||||
deadline := time.Now().Add(time.Second)
|
||||
for buffer.String() == "" && time.Now().Before(deadline) {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
var entry struct {
|
||||
Level string `json:"level"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(buffer.String()), &entry); err != nil {
|
||||
t.Fatalf("log line %q: %v", buffer.String(), err)
|
||||
}
|
||||
if entry.Msg != "request" || entry.Level != tt.want {
|
||||
t.Errorf("logged %+v; want a request at %s", entry, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user