Files
blog-server/internal/middleware/logger.go
2026-07-22 17:50:33 +08:00

104 lines
2.1 KiB
Go

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...)
}
})
}