78 lines
2.0 KiB
Go
78 lines
2.0 KiB
Go
|
|
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
|
||
|
|
}
|