192 lines
4.9 KiB
Go
192 lines
4.9 KiB
Go
package logger
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// RequestLoggerMiddleware creates a Gin middleware for request logging
|
|
func RequestLoggerMiddleware(logger *Logger) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Generate request ID if not present
|
|
requestID := c.GetHeader("X-Request-ID")
|
|
if requestID == "" {
|
|
requestID = uuid.New().String()
|
|
c.Header("X-Request-ID", requestID)
|
|
}
|
|
|
|
// Get correlation ID
|
|
correlationID := c.GetHeader("X-Correlation-ID")
|
|
if correlationID == "" {
|
|
correlationID = uuid.New().String()
|
|
c.Header("X-Correlation-ID", correlationID)
|
|
}
|
|
|
|
// Create request-scoped logger
|
|
reqLogger := logger.
|
|
WithRequestID(requestID).
|
|
WithCorrelationID(correlationID)
|
|
|
|
// Store logger in context
|
|
c.Set("logger", reqLogger)
|
|
c.Set("request_id", requestID)
|
|
c.Set("correlation_id", correlationID)
|
|
|
|
// Capture request body for logging if needed
|
|
var requestBody []byte
|
|
if c.Request.Body != nil && strings.HasPrefix(c.ContentType(), "application/json") {
|
|
requestBody, _ = io.ReadAll(c.Request.Body)
|
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
|
}
|
|
|
|
// Start timer
|
|
start := time.Now()
|
|
|
|
// Log request start
|
|
reqLogger.Info("Request started", map[string]interface{}{
|
|
"method": c.Request.Method,
|
|
"path": c.Request.URL.Path,
|
|
"query": c.Request.URL.RawQuery,
|
|
"remote_addr": c.Request.RemoteAddr,
|
|
"user_agent": c.Request.UserAgent(),
|
|
"content_type": c.ContentType(),
|
|
"body_size": len(requestBody),
|
|
})
|
|
|
|
// Process request
|
|
c.Next()
|
|
|
|
// Calculate duration
|
|
duration := time.Since(start)
|
|
|
|
// Get response status
|
|
status := c.Writer.Status()
|
|
responseSize := c.Writer.Size()
|
|
|
|
// Log level based on status code
|
|
var logLevel LogLevel
|
|
switch {
|
|
case status >= 500:
|
|
logLevel = ERROR
|
|
case status >= 400:
|
|
logLevel = WARN
|
|
default:
|
|
logLevel = INFO
|
|
}
|
|
|
|
// Log request completion
|
|
fields := map[string]interface{}{
|
|
"method": c.Request.Method,
|
|
"path": c.Request.URL.Path,
|
|
"status": status,
|
|
"duration": duration.String(),
|
|
"duration_ms": duration.Milliseconds(),
|
|
"response_size": responseSize,
|
|
"client_ip": c.ClientIP(),
|
|
"user_agent": c.Request.UserAgent(),
|
|
"content_type": c.ContentType(),
|
|
"content_length": c.Request.ContentLength,
|
|
}
|
|
|
|
// Add query parameters if present
|
|
if c.Request.URL.RawQuery != "" {
|
|
fields["query"] = c.Request.URL.RawQuery
|
|
}
|
|
|
|
// Add error information if present
|
|
if len(c.Errors) > 0 {
|
|
errors := make([]string, len(c.Errors))
|
|
for i, err := range c.Errors {
|
|
errors[i] = err.Error()
|
|
}
|
|
fields["errors"] = errors
|
|
}
|
|
|
|
reqLogger.log(logLevel, "Request completed", &duration, fields)
|
|
}
|
|
}
|
|
|
|
// GetLoggerFromContext retrieves the logger from Gin context
|
|
func GetLoggerFromContext(c *gin.Context) *Logger {
|
|
if logger, exists := c.Get("logger"); exists {
|
|
if l, ok := logger.(*Logger); ok {
|
|
return l
|
|
}
|
|
}
|
|
return globalLogger
|
|
}
|
|
|
|
// GetRequestIDFromContext retrieves the request ID from Gin context
|
|
func GetRequestIDFromContext(c *gin.Context) string {
|
|
if requestID, exists := c.Get("request_id"); exists {
|
|
if id, ok := requestID.(string); ok {
|
|
return id
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetCorrelationIDFromContext retrieves the correlation ID from Gin context
|
|
func GetCorrelationIDFromContext(c *gin.Context) string {
|
|
if correlationID, exists := c.Get("correlation_id"); exists {
|
|
if id, ok := correlationID.(string); ok {
|
|
return id
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// DatabaseLoggerMiddleware creates middleware for database operation logging
|
|
func DatabaseLoggerMiddleware(logger *Logger, serviceName string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
reqLogger := GetLoggerFromContext(c).WithService(serviceName)
|
|
c.Set("db_logger", reqLogger)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// GetDBLoggerFromContext retrieves the database logger from Gin context
|
|
func GetDBLoggerFromContext(c *gin.Context) *Logger {
|
|
if logger, exists := c.Get("db_logger"); exists {
|
|
if l, ok := logger.(*Logger); ok {
|
|
return l
|
|
}
|
|
}
|
|
return GetLoggerFromContext(c)
|
|
}
|
|
|
|
// ServiceLogger creates a service-specific logger
|
|
func ServiceLogger(serviceName string) *Logger {
|
|
return globalLogger.WithService(serviceName)
|
|
}
|
|
|
|
// AuthServiceLogger returns a logger for auth service
|
|
func AuthServiceLogger() *Logger {
|
|
return ServiceLogger("auth-service")
|
|
}
|
|
|
|
// BPJSServiceLogger returns a logger for BPJS service
|
|
func BPJSServiceLogger() *Logger {
|
|
return ServiceLogger("bpjs-service")
|
|
}
|
|
|
|
// RetribusiServiceLogger returns a logger for retribusi service
|
|
func RetribusiServiceLogger() *Logger {
|
|
return ServiceLogger("retribusi-service")
|
|
}
|
|
|
|
// DatabaseServiceLogger returns a logger for database operations
|
|
func DatabaseServiceLogger() *Logger {
|
|
return ServiceLogger("database-service")
|
|
}
|
|
|
|
// MiddlewareServiceLogger returns a logger for middleware operations
|
|
func MiddlewareServiceLogger() *Logger {
|
|
return ServiceLogger("middleware-service")
|
|
}
|