package middleware import ( "bytes" "io" "strings" "time" "github.com/gin-gonic/gin" "zeromesh/internal/model" ) type LogWriter interface { InsertApiLog(*model.ApiLog) error InsertErrorLog(*model.ErrorLog) error } type RequestLogger struct { writer LogWriter } func NewRequestLogger(writer LogWriter) *RequestLogger { return &RequestLogger{writer: writer} } func (l *RequestLogger) Handler() gin.HandlerFunc { return func(c *gin.Context) { path := c.Request.URL.Path if strings.HasPrefix(path, "/api/v1/log") { c.Next() return } start := time.Now() body := "" if c.Request.Body != nil { b, _ := io.ReadAll(c.Request.Body) body = string(b) c.Request.Body = io.NopCloser(bytes.NewBuffer(b)) } blw := &bodyLogWriter{ResponseWriter: c.Writer, buf: &bytes.Buffer{}} c.Writer = blw c.Next() latencyMs := time.Since(start).Milliseconds() ip := c.ClientIP() method := c.Request.Method query := c.Request.URL.RawQuery ua := c.Request.UserAgent() status := c.Writer.Status() if status >= 400 { el := &model.ErrorLog{ IP: ip, Method: method, Path: path, StatusCode: status, Query: query, UserAgent: ua, RequestBody: body, ResponseBody: blw.buf.String(), LatencyMs: latencyMs, } if err := l.writer.InsertErrorLog(el); err != nil { c.Error(err) } } al := &model.ApiLog{ IP: ip, Method: method, Path: path, StatusCode: status, Query: query, UserAgent: ua, LatencyMs: latencyMs, } if err := l.writer.InsertApiLog(al); err != nil { c.Error(err) } } } type bodyLogWriter struct { gin.ResponseWriter buf *bytes.Buffer } func (w *bodyLogWriter) Write(b []byte) (int, error) { w.buf.Write(b) return w.ResponseWriter.Write(b) }