Files

187 lines
4.9 KiB
Go
Raw Permalink Normal View History

2025-12-19 22:36:48 +08:00
package middleware
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/gin-gonic/gin"
)
// responseWriter 包装 gin.ResponseWriter 以捕获响应体
type responseWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w responseWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
// RequestLogger API请求和响应日志中间件
func RequestLogger() gin.HandlerFunc {
return func(c *gin.Context) {
startTime := time.Now()
2026-01-06 19:36:42 +08:00
// 读取请求体(跳过文件上传)
2025-12-19 22:36:48 +08:00
var requestBody []byte
2026-01-06 19:36:42 +08:00
contentType := c.GetHeader("Content-Type")
// 如果不是文件上传,才读取请求体
if c.Request.Body != nil && !strings.HasPrefix(contentType, "multipart/form-data") {
2025-12-19 22:36:48 +08:00
requestBody, _ = io.ReadAll(c.Request.Body)
// 恢复请求体供后续处理使用
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
}
// 包装 ResponseWriter 以捕获响应
blw := &responseWriter{
ResponseWriter: c.Writer,
body: bytes.NewBufferString(""),
}
c.Writer = blw
// 打印请求信息
printRequest(c, requestBody)
// 处理请求
c.Next()
// 计算请求耗时
duration := time.Since(startTime)
// 打印响应信息
printResponse(c, blw.body.Bytes(), duration)
}
}
// printRequest 打印请求详情
func printRequest(c *gin.Context, body []byte) {
fmt.Println("\n" + strings.Repeat("=", 100))
fmt.Printf("📥 [REQUEST] %s\n", time.Now().Format("2006-01-02 15:04:05"))
fmt.Println(strings.Repeat("=", 100))
2026-01-06 19:36:42 +08:00
2025-12-19 22:36:48 +08:00
// 请求基本信息
fmt.Printf("Method: %s\n", c.Request.Method)
fmt.Printf("Path: %s\n", c.Request.URL.Path)
fmt.Printf("Full URL: %s\n", c.Request.URL.String())
fmt.Printf("Client IP: %s\n", c.ClientIP())
fmt.Printf("User-Agent: %s\n", c.Request.UserAgent())
2026-01-06 19:36:42 +08:00
2025-12-19 22:36:48 +08:00
// 请求头
if len(c.Request.Header) > 0 {
fmt.Println("\n--- Headers ---")
for key, values := range c.Request.Header {
// 过滤敏感信息
if strings.ToLower(key) == "authorization" || strings.ToLower(key) == "cookie" {
fmt.Printf("%s: [HIDDEN]\n", key)
} else {
fmt.Printf("%s: %s\n", key, strings.Join(values, ", "))
}
}
}
// 查询参数
if len(c.Request.URL.Query()) > 0 {
fmt.Println("\n--- Query Parameters ---")
for key, values := range c.Request.URL.Query() {
fmt.Printf("%s: %s\n", key, strings.Join(values, ", "))
}
}
// 请求体
if len(body) > 0 {
fmt.Println("\n--- Request Body ---")
// 尝试格式化 JSON
var prettyJSON bytes.Buffer
if err := json.Indent(&prettyJSON, body, "", " "); err == nil {
fmt.Println(prettyJSON.String())
} else {
fmt.Println(string(body))
}
2026-01-06 19:36:42 +08:00
} else if strings.HasPrefix(c.GetHeader("Content-Type"), "multipart/form-data") {
fmt.Println("\n--- Request Body ---")
fmt.Println("[File upload: multipart/form-data]")
2025-12-19 22:36:48 +08:00
}
fmt.Println(strings.Repeat("-", 100))
}
// printResponse 打印响应详情
func printResponse(c *gin.Context, body []byte, duration time.Duration) {
fmt.Println("\n" + strings.Repeat("=", 100))
fmt.Printf("📤 [RESPONSE] %s | Duration: %v\n", time.Now().Format("2006-01-02 15:04:05"), duration)
fmt.Println(strings.Repeat("=", 100))
2026-01-06 19:36:42 +08:00
2025-12-19 22:36:48 +08:00
// 响应基本信息
fmt.Printf("Status Code: %d %s\n", c.Writer.Status(), getStatusText(c.Writer.Status()))
fmt.Printf("Size: %d bytes\n", c.Writer.Size())
2026-01-06 19:36:42 +08:00
2025-12-19 22:36:48 +08:00
// 响应头
if len(c.Writer.Header()) > 0 {
fmt.Println("\n--- Response Headers ---")
for key, values := range c.Writer.Header() {
fmt.Printf("%s: %s\n", key, strings.Join(values, ", "))
}
}
// 响应体
if len(body) > 0 {
fmt.Println("\n--- Response Body ---")
2026-01-06 19:36:42 +08:00
// 检查Content-Type跳过二进制数据
contentType := c.Writer.Header().Get("Content-Type")
if strings.Contains(contentType, "image/") ||
strings.Contains(contentType, "application/octet-stream") ||
len(body) > 10240 { // 超过10KB的响应不打印
fmt.Printf("[Binary data: %d bytes, Content-Type: %s]\n", len(body), contentType)
2025-12-19 22:36:48 +08:00
} else {
2026-01-06 19:36:42 +08:00
// 尝试格式化 JSON
var prettyJSON bytes.Buffer
if err := json.Indent(&prettyJSON, body, "", " "); err == nil {
fmt.Println(prettyJSON.String())
} else {
fmt.Println(string(body))
}
2025-12-19 22:36:48 +08:00
}
}
// 性能提示
if duration > 1*time.Second {
fmt.Printf("\n⚠ WARNING: Request took %.2f seconds (>1s)\n", duration.Seconds())
} else if duration > 500*time.Millisecond {
fmt.Printf("\n⚡ NOTICE: Request took %.0f milliseconds (>500ms)\n", duration.Milliseconds())
}
fmt.Println(strings.Repeat("=", 100))
fmt.Println()
}
// getStatusText 获取状态码文本
func getStatusText(code int) string {
switch code {
case 200:
return "OK"
case 201:
return "Created"
case 204:
return "No Content"
case 400:
return "Bad Request"
case 401:
return "Unauthorized"
case 403:
return "Forbidden"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
default:
return ""
}
}