Напиши HTTP-middleware: перед вызовом хендлера оно пишет в лог метод и путь
запроса, после — код ответа и тело.
func Logging(logger *log.Logger, next http.Handler) http.Handler
Формат строк лога (каждая — один вызов logger.Printf):
start GET /api/users
done GET /api/users status=201 body="{\"id\":1}"
Тело выводится через %q.
Требования
- Строка
start … пишется до вызова next, строка done … — после.
- Клиент получает от сервера ровно то же, что отдал хендлер: статус,
заголовки, тело.
- Если хендлер не вызвал
WriteHeader, статус — 200. Если хендлер ничего не
записал — status=200 body="".
- Учитывается первый выставленный статус, как это делает
net/http:
Write до WriteHeader(404) означает 200, повторный WriteHeader
игнорируется.
- Middleware работает при конкурентных запросах. Код проходит
go test -race.
Пример
mux := http.NewServeMux()
mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
})
http.ListenAndServe(":8080", Logging(log.Default(), mux))
// start GET /ping
// done GET /ping status=200 body="pong"
Хендлеры, которые отдают данные потоком (SSE, длинные выгрузки), делают
w.(http.Flusher).Flush(). Обёртка над ResponseWriter не должна это ломать:
- Если исходный
ResponseWriter умеет Flush, то и тот, что получает хендлер,
умеет, и Flush доходит до клиента.
Устно: логировать тело целиком в проде — хорошая идея? Что делать с
бинарными и огромными ответами, с персональными данными? Зачем Unwrap() у
обёртки и что такое http.ResponseController?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.