"net/http"
"net/textproto"
"strings"
+ "time"
)
const (
kPath = "path"
kUserAgent = "user_agent"
kStatusCode = "status"
+ kTimeTaken = "duration_ms"
+ kBytesWritten = "response_size"
)
type statusRecorder struct {
http.ResponseWriter
http.Hijacker
- Status int
+ Status int
+ BytesWritten uint
}
type LoggingMiddleware struct {
- Logger Logger
+ Logger coreLogger
ExtraFunc func(map[string]any)
extraRequestHeaders []string
func (lm *LoggingMiddleware) handle(w http.ResponseWriter, r *http.Request) {
ws := &statusRecorder{
ResponseWriter: w,
+ Status: http.StatusOK,
}
if h, ok := w.(http.Hijacker); ok {
ws.Hijacker = h
}
+ startTime := time.Now().UnixMilli()
lm.h.ServeHTTP(ws, r)
+ respTime := time.Now().UnixMilli() - startTime
entry := map[string]any{
kRemoteAddress: r.RemoteAddr,
kMethod: r.Method,
kPath: r.URL.Path,
kStatusCode: ws.Status,
+ kTimeTaken: respTime,
+ kBytesWritten: ws.BytesWritten,
}
for _, h := range lm.extraRequestHeaders {
- hKey := strings.ReplaceAll("-", "_", h)
+ hKey := strings.ReplaceAll(h, "-", "_")
entry[hKey] = r.Header.Get(h)
}
for _, h := range lm.extraResponseHeaders {
- hKey := strings.ReplaceAll("-", "_", h)
- entry[hKey] = r.Header.Get(h)
+ hKey := strings.ReplaceAll(h, "-", "_")
+ entry[hKey] = w.Header().Get(h)
}
if lm.ExtraFunc != nil {
lm.ExtraFunc(entry)
}
func (r *statusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- r.Status = http.StatusSwitchingProtocols
+ if r.Status == 0 {
+ r.Status = http.StatusSwitchingProtocols
+ }
return r.Hijacker.Hijack()
}
+
+func (r *statusRecorder) Write(buf []byte) (int, error) {
+ n, err := r.ResponseWriter.Write(buf)
+ r.BytesWritten += uint(n)
+ return n, err
+}