feat: add a middleware to log requests
This commit is contained in:
commit
44199b12c0
3 changed files with 88 additions and 0 deletions
2
doc.go
Normal file
2
doc.go
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package mw provides a collection of middlewares for net/http.
|
||||
package mw
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module code.bcarlin.net/go/mw
|
||||
|
||||
go 1.20
|
83
log_request.go
Normal file
83
log_request.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package mw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
logRequest = "request"
|
||||
logResponse = "response"
|
||||
logMethod = "method"
|
||||
logURI = "uri"
|
||||
logStatus = "status"
|
||||
logSize = "size"
|
||||
logDuration = "duration"
|
||||
)
|
||||
|
||||
// LoggingMiddleware returns a middleware that logs the request and response,
|
||||
// each in a loggroup.
|
||||
//
|
||||
// The request group contains the request method and URI.
|
||||
// The response group contains the response status, size and duration.
|
||||
func LoggingMiddleware(l *slog.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
startTime := time.Now()
|
||||
|
||||
lwr := &loggingResponseWriter{
|
||||
ResponseWriter: w,
|
||||
data: &responseData{status: http.StatusOK, size: 0},
|
||||
}
|
||||
|
||||
next.ServeHTTP(lwr, r)
|
||||
|
||||
l.InfoContext(r.Context(), "HTTP request answered",
|
||||
slog.Group(
|
||||
logRequest,
|
||||
slog.String(logMethod, r.Method),
|
||||
slog.String(logURI, r.RequestURI),
|
||||
),
|
||||
slog.Group(
|
||||
logResponse,
|
||||
slog.Int(logStatus, lwr.data.status),
|
||||
slog.Int(logSize, lwr.data.size),
|
||||
slog.Duration(logDuration, time.Since(startTime)),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Struct for holding response details.
|
||||
type responseData struct {
|
||||
status int
|
||||
size int
|
||||
}
|
||||
|
||||
// loggingResponseWriter is a wrapper for http.ResponseWriter that records the
|
||||
// size of data sent and the status code of the response.
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter // compose original http.ResponseWriter
|
||||
data *responseData
|
||||
}
|
||||
|
||||
// Write implements http.ResponseWriter.
|
||||
func (r *loggingResponseWriter) Write(b []byte) (int, error) {
|
||||
size, err := r.ResponseWriter.Write(b)
|
||||
r.data.size += size
|
||||
|
||||
if err != nil {
|
||||
return size, fmt.Errorf("cannot forward data: %w", err)
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// WriteHeader implements http.ResponseWriter.
|
||||
func (r *loggingResponseWriter) WriteHeader(statusCode int) {
|
||||
r.ResponseWriter.WriteHeader(statusCode)
|
||||
r.data.status = statusCode
|
||||
}
|
Loading…
Reference in a new issue