Files

78 lines
2.0 KiB
Go
Raw Permalink Normal View History

2026-07-30 09:51:25 +07:00
package middleware
import (
"bytes"
"encoding/json"
"io"
"time"
"cardverse/internal/pkg/logger"
"github.com/gin-gonic/gin"
)
// Logger mencatat setiap request: method, path, status, durasi, IP, dan body request
// (kalau JSON) supaya gampang di-debug. Field sensitif seperti password, token, dsb
// OTOMATIS disensor oleh package logger (lihat internal/pkg/logger), jadi aman
// dipasang bahkan di endpoint login/register.
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
bodyFields := readBodyForLog(c)
c.Next()
duration := time.Since(start)
status := c.Writer.Status()
fields := logger.Fields{
"method": c.Request.Method,
"path": c.Request.URL.Path,
"status": status,
"duration": duration.String(),
"ip": c.ClientIP(),
}
if bodyFields != nil {
fields["body"] = bodyFields
}
if len(c.Errors) > 0 {
fields["gin_errors"] = c.Errors.String()
}
switch {
case status >= 500:
logger.Error("request selesai", fields)
case status >= 400:
logger.Warn("request selesai", fields)
default:
logger.Info("request selesai", fields)
}
}
}
// readBodyForLog membaca body request (kalau berupa JSON) untuk keperluan log,
// lalu mengembalikan body itu ke request supaya handler asli tetap bisa membacanya
// seperti biasa. Field sensitif di dalam body (misal "password") akan disensor
// otomatis oleh package logger saat log ditulis, bukan di sini.
func readBodyForLog(c *gin.Context) map[string]interface{} {
if c.Request.Body == nil {
return nil
}
if c.Request.Method != "POST" && c.Request.Method != "PUT" && c.Request.Method != "PATCH" {
return nil
}
bodyBytes, err := io.ReadAll(c.Request.Body)
if err != nil {
return nil
}
// kembalikan body supaya bisa dibaca lagi oleh ShouldBindJSON di handler
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
var parsed map[string]interface{}
if err := json.Unmarshal(bodyBytes, &parsed); err != nil {
return nil // bukan JSON (atau body kosong), tidak masalah, cukup skip
}
return parsed
}