96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/seifghazi/claude-code-monitor/internal/model"
|
|
)
|
|
|
|
func Logging(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
start := time.Now()
|
|
|
|
// For POST requests with body, read and store the bytes
|
|
var bodyBytes []byte
|
|
if r.Body != nil && (r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH") {
|
|
var err error
|
|
bodyBytes, err = io.ReadAll(r.Body)
|
|
if err != nil {
|
|
log.Printf("❌ Error reading request body: %v", err)
|
|
http.Error(w, "Error reading request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
r.Body.Close()
|
|
r.Body = io.NopCloser(bytes.NewReader(bodyBytes))
|
|
|
|
// Store raw bytes in context for handler to use
|
|
ctx := context.WithValue(r.Context(), model.BodyBytesKey, bodyBytes)
|
|
r = r.WithContext(ctx)
|
|
}
|
|
|
|
wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
|
next.ServeHTTP(wrapped, r)
|
|
|
|
duration := time.Since(start)
|
|
statusColor := getStatusColor(wrapped.statusCode)
|
|
|
|
log.Printf("%s %s %s%d%s %s (%s)",
|
|
r.Method,
|
|
r.URL.Path,
|
|
statusColor,
|
|
wrapped.statusCode,
|
|
colorReset,
|
|
http.StatusText(wrapped.statusCode),
|
|
formatDuration(duration))
|
|
})
|
|
}
|
|
|
|
type responseWriter struct {
|
|
http.ResponseWriter
|
|
statusCode int
|
|
}
|
|
|
|
func (rw *responseWriter) WriteHeader(code int) {
|
|
rw.statusCode = code
|
|
rw.ResponseWriter.WriteHeader(code)
|
|
}
|
|
|
|
// ANSI color codes
|
|
const (
|
|
colorReset = "\033[0m"
|
|
colorGreen = "\033[32m"
|
|
colorYellow = "\033[33m"
|
|
colorRed = "\033[31m"
|
|
colorBlue = "\033[34m"
|
|
colorCyan = "\033[36m"
|
|
)
|
|
|
|
func getStatusColor(status int) string {
|
|
switch {
|
|
case status >= 200 && status < 300:
|
|
return colorGreen
|
|
case status >= 300 && status < 400:
|
|
return colorBlue
|
|
case status >= 400 && status < 500:
|
|
return colorYellow
|
|
case status >= 500:
|
|
return colorRed
|
|
default:
|
|
return colorReset
|
|
}
|
|
}
|
|
|
|
func formatDuration(d time.Duration) string {
|
|
if d < time.Millisecond {
|
|
return fmt.Sprintf("%dµs", d.Microseconds())
|
|
} else if d < time.Second {
|
|
return fmt.Sprintf("%dms", d.Milliseconds())
|
|
}
|
|
return fmt.Sprintf("%.2fs", d.Seconds())
|
|
}
|