package middleware import ( "encoding/json" "log/slog" "net/http" "server/internal/model/common" "server/internal/utils" "time" gonanoid "github.com/matoous/go-nanoid/v2" ) type LoggerMiddleware struct { } type responseWriter struct { http.ResponseWriter statusCode int bytes int errorMsg string } func (rw *responseWriter) WriteHeader(code int) { rw.statusCode = code rw.ResponseWriter.WriteHeader(code) } func generateRequestID() string { id, _ := gonanoid.New(16) // 16字符 return id } // extractErrorMessage 从响应体中提取错误信息 func (rw *responseWriter) extractErrorMessage(body []byte) string { var resp common.Response if err := json.Unmarshal(body, &resp); err == nil && resp.Message != "" { return resp.Message } return string(body) } func (rw *responseWriter) Write(b []byte) (int, error) { n, err := rw.ResponseWriter.Write(b) rw.bytes += n // 只在错误状态码且未记录错误时处理 if rw.statusCode >= 400 && rw.errorMsg == "" && n > 0 { rw.errorMsg = rw.extractErrorMessage(b[:n]) } return n, err } func NewLoggerMiddleware() *LoggerMiddleware { return &LoggerMiddleware{} } func (m *LoggerMiddleware) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() reqID := generateRequestID() wrapped := &responseWriter{ ResponseWriter: w, statusCode: http.StatusOK, } next.ServeHTTP(wrapped, r) duration := time.Since(start) ip := utils.ClientIP(r) fullPath := r.URL.Path if r.URL.RawQuery != "" { fullPath = fullPath + "?" + r.URL.RawQuery } fields := []any{ "request_id", reqID, "method", r.Method, "path", fullPath, "status", wrapped.statusCode, "duration", duration, "client_ip", ip, } if wrapped.errorMsg != "" { fields = append(fields, "error", wrapped.errorMsg) } switch { case wrapped.statusCode >= 500: slog.Error("request", fields...) case wrapped.statusCode >= 400: slog.Warn("request", fields...) default: slog.Info("request", fields...) } }) }