Perbaikan Middelware dan tool generete logger
This commit is contained in:
@@ -7,10 +7,10 @@ import (
|
||||
modelsretribusi "api-service/internal/models/retribusi"
|
||||
utils "api-service/internal/utils/filters"
|
||||
"api-service/internal/utils/validation"
|
||||
"api-service/pkg/logger"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -38,7 +38,7 @@ func init() {
|
||||
validate.RegisterValidation("retribusi_status", validateRetribusiStatus)
|
||||
|
||||
if db == nil {
|
||||
log.Fatal("Failed to initialize database connection")
|
||||
logger.Fatal("Failed to initialize database connection")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -830,7 +830,10 @@ func (h *RetribusiHandler) deleteRetribusi(ctx context.Context, dbConn *sql.DB,
|
||||
|
||||
// Enhanced error handling
|
||||
func (h *RetribusiHandler) logAndRespondError(c *gin.Context, message string, err error, statusCode int) {
|
||||
log.Printf("[ERROR] %s: %v", message, err)
|
||||
logger.Error(message, map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"status_code": statusCode,
|
||||
})
|
||||
h.respondError(c, message, err, statusCode)
|
||||
}
|
||||
|
||||
@@ -878,7 +881,10 @@ func (h *RetribusiHandler) parsePaginationParams(c *gin.Context) (int, int, erro
|
||||
offset = parsedOffset
|
||||
}
|
||||
|
||||
log.Printf("Pagination - Limit: %d, Offset: %d", limit, offset)
|
||||
logger.Debug("Pagination parameters", map[string]interface{}{
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
return limit, offset, nil
|
||||
}
|
||||
|
||||
@@ -1257,7 +1263,11 @@ func (h *RetribusiHandler) fetchRetribusis(ctx context.Context, dbConn *sql.DB,
|
||||
return nil, fmt.Errorf("rows iteration error: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Successfully fetched %d retribusis with filters applied", len(retribusis))
|
||||
logger.Info("Successfully fetched retribusis", map[string]interface{}{
|
||||
"count": len(retribusis),
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
return retribusis, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
retribusiHandlers "api-service/internal/handlers/retribusi"
|
||||
"api-service/internal/middleware"
|
||||
services "api-service/internal/services/auth"
|
||||
"log"
|
||||
"api-service/pkg/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
@@ -23,13 +23,13 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
||||
// Add global middleware
|
||||
router.Use(middleware.CORSConfig())
|
||||
router.Use(middleware.ErrorHandler())
|
||||
router.Use(gin.Logger())
|
||||
router.Use(logger.RequestLoggerMiddleware(logger.Default()))
|
||||
router.Use(gin.Recovery())
|
||||
|
||||
// Initialize services with error handling
|
||||
authService := services.NewAuthService(cfg)
|
||||
if authService == nil {
|
||||
log.Fatal("Failed to initialize auth service")
|
||||
logger.Fatal("Failed to initialize auth service")
|
||||
}
|
||||
|
||||
// Swagger UI route
|
||||
|
||||
356
pkg/logger/README.md
Normal file
356
pkg/logger/README.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# Structured Logger Package
|
||||
|
||||
A comprehensive structured logging package for Go applications with support for different log levels, service-specific logging, request context, and JSON output formatting.
|
||||
|
||||
## Features
|
||||
|
||||
- **Structured Logging**: JSON and text format output with rich metadata
|
||||
- **Multiple Log Levels**: DEBUG, INFO, WARN, ERROR, FATAL
|
||||
- **Service-Specific Logging**: Dedicated loggers for different services
|
||||
- **Request Context**: Request ID and correlation ID tracking
|
||||
- **Performance Timing**: Built-in duration logging for operations
|
||||
- **Gin Middleware**: Request logging middleware for HTTP requests
|
||||
- **Environment Configuration**: Configurable via environment variables
|
||||
|
||||
## Installation
|
||||
|
||||
The logger is already integrated into the project. Import it using:
|
||||
|
||||
```go
|
||||
import "api-service/pkg/logger"
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```go
|
||||
// Global functions (use default logger)
|
||||
logger.Info("Application starting")
|
||||
logger.Error("Something went wrong", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"code": "DB_CONNECTION_FAILED",
|
||||
})
|
||||
|
||||
// Create a service-specific logger
|
||||
authLogger := logger.ServiceLogger("auth-service")
|
||||
authLogger.Info("User authenticated", map[string]interface{}{
|
||||
"user_id": "123",
|
||||
"method": "oauth2",
|
||||
})
|
||||
```
|
||||
|
||||
### Service-Specific Loggers
|
||||
|
||||
```go
|
||||
// Pre-defined service loggers
|
||||
authLogger := logger.AuthServiceLogger()
|
||||
bpjsLogger := logger.BPJSServiceLogger()
|
||||
retribusiLogger := logger.RetribusiServiceLogger()
|
||||
databaseLogger := logger.DatabaseServiceLogger()
|
||||
|
||||
authLogger.Info("Authentication successful")
|
||||
databaseLogger.Debug("Query executed", map[string]interface{}{
|
||||
"query": "SELECT * FROM users",
|
||||
"time": "150ms",
|
||||
})
|
||||
```
|
||||
|
||||
### Request Context Logging
|
||||
|
||||
```go
|
||||
// Add request context to logs
|
||||
requestLogger := logger.Default().
|
||||
WithRequestID("req-123456").
|
||||
WithCorrelationID("corr-789012").
|
||||
WithField("user_id", "user-123")
|
||||
|
||||
requestLogger.Info("Request processing started", map[string]interface{}{
|
||||
"endpoint": "/api/v1/data",
|
||||
"method": "POST",
|
||||
})
|
||||
```
|
||||
|
||||
### Performance Timing
|
||||
|
||||
```go
|
||||
// Time operations and log duration
|
||||
start := time.Now()
|
||||
// ... perform operation ...
|
||||
logger.LogDuration(start, "Database query completed", map[string]interface{}{
|
||||
"query": "SELECT * FROM large_table",
|
||||
"rows": 1000,
|
||||
"database": "postgres",
|
||||
})
|
||||
```
|
||||
|
||||
## Gin Middleware Integration
|
||||
|
||||
### Add Request Logger Middleware
|
||||
|
||||
In your routes setup:
|
||||
|
||||
```go
|
||||
import "api-service/pkg/logger"
|
||||
|
||||
func RegisterRoutes(cfg *config.Config) *gin.Engine {
|
||||
router := gin.New()
|
||||
|
||||
// Add request logging middleware
|
||||
router.Use(logger.RequestLoggerMiddleware(logger.Default()))
|
||||
|
||||
// ... other middleware and routes
|
||||
return router
|
||||
}
|
||||
```
|
||||
|
||||
### Access Logger in Handlers
|
||||
|
||||
```go
|
||||
func (h *MyHandler) MyEndpoint(c *gin.Context) {
|
||||
// Get logger from context
|
||||
logger := logger.GetLoggerFromContext(c)
|
||||
|
||||
logger.Info("Endpoint called", map[string]interface{}{
|
||||
"user_agent": c.Request.UserAgent(),
|
||||
"client_ip": c.ClientIP(),
|
||||
})
|
||||
|
||||
// Get request IDs
|
||||
requestID := logger.GetRequestIDFromContext(c)
|
||||
correlationID := logger.GetCorrelationIDFromContext(c)
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Set these environment variables to configure the logger:
|
||||
|
||||
```bash
|
||||
# Log level (DEBUG, INFO, WARN, ERROR, FATAL)
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# Output format (text or json)
|
||||
LOG_FORMAT=text
|
||||
|
||||
# Service name for logs
|
||||
LOG_SERVICE=api-service
|
||||
|
||||
# Enable JSON format
|
||||
LOG_JSON=false
|
||||
```
|
||||
|
||||
### Programmatic Configuration
|
||||
|
||||
```go
|
||||
// Create custom logger with specific configuration
|
||||
cfg := logger.Config{
|
||||
Level: "DEBUG",
|
||||
JSONFormat: true,
|
||||
Service: "my-custom-service",
|
||||
}
|
||||
|
||||
customLogger := logger.NewFromConfig(cfg)
|
||||
|
||||
// Or create manually
|
||||
logger := logger.New("service-name", logger.DEBUG, true)
|
||||
```
|
||||
|
||||
## Log Levels
|
||||
|
||||
| Level | Description | Usage |
|
||||
|-------|-------------|-------|
|
||||
| DEBUG | Detailed debug information | Development and troubleshooting |
|
||||
| INFO | General operational messages | Normal application behavior |
|
||||
| WARN | Warning conditions | Something unexpected but not an error |
|
||||
| ERROR | Error conditions | Operation failed but application continues |
|
||||
| FATAL | Critical conditions | Application cannot continue |
|
||||
|
||||
## Output Formats
|
||||
|
||||
### Text Format (Default)
|
||||
```
|
||||
2025-08-22T04:33:12+07:00 [INFO] auth-service: User authentication successful (handler/auth.go:45) [user_id=12345 method=oauth2]
|
||||
```
|
||||
|
||||
### JSON Format
|
||||
```json
|
||||
{
|
||||
"timestamp": "2025-08-22T04:33:12+07:00",
|
||||
"level": "INFO",
|
||||
"service": "auth-service",
|
||||
"message": "User authentication successful",
|
||||
"file": "handler/auth.go",
|
||||
"line": 45,
|
||||
"request_id": "req-123456",
|
||||
"correlation_id": "corr-789012",
|
||||
"fields": {
|
||||
"user_id": "12345",
|
||||
"method": "oauth2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Appropriate Log Levels
|
||||
```go
|
||||
// Good
|
||||
logger.Debug("Detailed debug info")
|
||||
logger.Info("User action completed")
|
||||
logger.Warn("Rate limit approaching")
|
||||
logger.Error("Database connection failed")
|
||||
|
||||
// Avoid
|
||||
logger.Info("Error connecting to database") // Use ERROR instead
|
||||
```
|
||||
|
||||
### 2. Add Context to Logs
|
||||
```go
|
||||
// Instead of this:
|
||||
logger.Error("Login failed")
|
||||
|
||||
// Do this:
|
||||
logger.Error("Login failed", map[string]interface{}{
|
||||
"username": username,
|
||||
"reason": "invalid_credentials",
|
||||
"attempts": loginAttempts,
|
||||
"client_ip": clientIP,
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Use Service-Specific Loggers
|
||||
```go
|
||||
// Create once per service
|
||||
var authLogger = logger.AuthServiceLogger()
|
||||
|
||||
func LoginHandler(c *gin.Context) {
|
||||
authLogger.Info("Login attempt", map[string]interface{}{
|
||||
"username": c.PostForm("username"),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Measure Performance
|
||||
```go
|
||||
func ProcessData(data []byte) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
logger.LogDuration(start, "Data processing completed", map[string]interface{}{
|
||||
"data_size": len(data),
|
||||
"items": countItems(data),
|
||||
})
|
||||
}()
|
||||
|
||||
// ... processing logic ...
|
||||
}
|
||||
```
|
||||
|
||||
## Migration from Standard Log Package
|
||||
|
||||
### Before (standard log)
|
||||
```go
|
||||
import "log"
|
||||
|
||||
log.Printf("Error: %v", err)
|
||||
log.Printf("User %s logged in", username)
|
||||
```
|
||||
|
||||
### After (structured logger)
|
||||
```go
|
||||
import "api-service/pkg/logger"
|
||||
|
||||
logger.Error("Operation failed", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"context": "user_login",
|
||||
})
|
||||
|
||||
logger.Info("User logged in", map[string]interface{}{
|
||||
"username": username,
|
||||
"method": "password",
|
||||
})
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Database Operations
|
||||
```go
|
||||
func (h *UserHandler) GetUser(c *gin.Context) {
|
||||
logger := logger.GetLoggerFromContext(c)
|
||||
start := time.Now()
|
||||
|
||||
user, err := h.db.GetUser(c.Param("id"))
|
||||
if err != nil {
|
||||
logger.Error("Failed to get user", map[string]interface{}{
|
||||
"user_id": c.Param("id"),
|
||||
"error": err.Error(),
|
||||
})
|
||||
c.JSON(500, gin.H{"error": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
logger.LogDuration(start, "User retrieved successfully", map[string]interface{}{
|
||||
"user_id": user.ID,
|
||||
"query_time": time.Since(start).String(),
|
||||
})
|
||||
|
||||
c.JSON(200, user)
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Service
|
||||
```go
|
||||
var authLogger = logger.AuthServiceLogger()
|
||||
|
||||
func Authenticate(username, password string) (bool, error) {
|
||||
authLogger.Debug("Authentication attempt", map[string]interface{}{
|
||||
"username": username,
|
||||
})
|
||||
|
||||
// Authentication logic...
|
||||
|
||||
if authenticated {
|
||||
authLogger.Info("Authentication successful", map[string]interface{}{
|
||||
"username": username,
|
||||
"method": "password",
|
||||
})
|
||||
return true, nil
|
||||
}
|
||||
|
||||
authLogger.Warn("Authentication failed", map[string]interface{}{
|
||||
"username": username,
|
||||
"reason": "invalid_credentials",
|
||||
})
|
||||
return false, nil
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **No logs appearing**: Check that log level is not set too high (e.g., ERROR when logging INFO)
|
||||
2. **JSON format not working**: Ensure `LOG_JSON=true` or logger is created with `jsonFormat: true`
|
||||
3. **Missing context**: Use `WithRequestID()` and `WithCorrelationID()` for request context
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug logging for development:
|
||||
|
||||
```bash
|
||||
export LOG_LEVEL=DEBUG
|
||||
export LOG_FORMAT=text
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- Logger is designed to be lightweight and fast
|
||||
- Context fields are only evaluated when the log level is enabled
|
||||
- JSON marshaling only occurs when JSON format is enabled
|
||||
- Consider log volume in production environments
|
||||
|
||||
## License
|
||||
|
||||
This logger package is part of the API Service project.
|
||||
137
pkg/logger/config.go
Normal file
137
pkg/logger/config.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config holds the configuration for the logger
|
||||
type Config struct {
|
||||
Level string `json:"level" default:"INFO"`
|
||||
JSONFormat bool `json:"json_format" default:"false"`
|
||||
Service string `json:"service" default:"api-service"`
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default logger configuration
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Level: "INFO",
|
||||
JSONFormat: false,
|
||||
Service: "api-service",
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfigFromEnv loads logger configuration from environment variables
|
||||
func LoadConfigFromEnv() Config {
|
||||
config := DefaultConfig()
|
||||
|
||||
// Load log level from environment
|
||||
if level := os.Getenv("LOG_LEVEL"); level != "" {
|
||||
config.Level = strings.ToUpper(level)
|
||||
}
|
||||
|
||||
// Load JSON format from environment
|
||||
if jsonFormat := os.Getenv("LOG_JSON_FORMAT"); jsonFormat != "" {
|
||||
if parsed, err := strconv.ParseBool(jsonFormat); err == nil {
|
||||
config.JSONFormat = parsed
|
||||
}
|
||||
}
|
||||
|
||||
// Load service name from environment
|
||||
if service := os.Getenv("LOG_SERVICE_NAME"); service != "" {
|
||||
config.Service = service
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// Validate validates the logger configuration
|
||||
func (c *Config) Validate() error {
|
||||
// Validate log level
|
||||
validLevels := map[string]bool{
|
||||
"DEBUG": true,
|
||||
"INFO": true,
|
||||
"WARN": true,
|
||||
"ERROR": true,
|
||||
"FATAL": true,
|
||||
}
|
||||
|
||||
if !validLevels[c.Level] {
|
||||
c.Level = "INFO" // Default to INFO if invalid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLogLevel returns the LogLevel from the configuration
|
||||
func (c *Config) GetLogLevel() LogLevel {
|
||||
switch strings.ToUpper(c.Level) {
|
||||
case "DEBUG":
|
||||
return DEBUG
|
||||
case "WARN":
|
||||
return WARN
|
||||
case "ERROR":
|
||||
return ERROR
|
||||
case "FATAL":
|
||||
return FATAL
|
||||
default:
|
||||
return INFO
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLoggerFromConfig creates a new logger instance from configuration
|
||||
func CreateLoggerFromConfig(cfg Config) *Logger {
|
||||
cfg.Validate()
|
||||
return NewFromConfig(cfg)
|
||||
}
|
||||
|
||||
// CreateLoggerFromEnv creates a new logger instance from environment variables
|
||||
func CreateLoggerFromEnv() *Logger {
|
||||
cfg := LoadConfigFromEnv()
|
||||
return CreateLoggerFromConfig(cfg)
|
||||
}
|
||||
|
||||
// Environment variable constants
|
||||
const (
|
||||
EnvLogLevel = "LOG_LEVEL"
|
||||
EnvLogJSONFormat = "LOG_JSON_FORMAT"
|
||||
EnvLogService = "LOG_SERVICE_NAME"
|
||||
)
|
||||
|
||||
// Service-specific configuration helpers
|
||||
|
||||
// AuthServiceConfig returns configuration for auth service
|
||||
func AuthServiceConfig() Config {
|
||||
cfg := LoadConfigFromEnv()
|
||||
cfg.Service = "auth-service"
|
||||
return cfg
|
||||
}
|
||||
|
||||
// BPJSServiceConfig returns configuration for BPJS service
|
||||
func BPJSServiceConfig() Config {
|
||||
cfg := LoadConfigFromEnv()
|
||||
cfg.Service = "bpjs-service"
|
||||
return cfg
|
||||
}
|
||||
|
||||
// RetribusiServiceConfig returns configuration for retribusi service
|
||||
func RetribusiServiceConfig() Config {
|
||||
cfg := LoadConfigFromEnv()
|
||||
cfg.Service = "retribusi-service"
|
||||
return cfg
|
||||
}
|
||||
|
||||
// DatabaseServiceConfig returns configuration for database service
|
||||
func DatabaseServiceConfig() Config {
|
||||
cfg := LoadConfigFromEnv()
|
||||
cfg.Service = "database-service"
|
||||
return cfg
|
||||
}
|
||||
|
||||
// MiddlewareServiceConfig returns configuration for middleware service
|
||||
func MiddlewareServiceConfig() Config {
|
||||
cfg := LoadConfigFromEnv()
|
||||
cfg.Service = "middleware-service"
|
||||
return cfg
|
||||
}
|
||||
142
pkg/logger/context.go
Normal file
142
pkg/logger/context.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// contextKey is a custom type for context keys to avoid collisions
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
loggerKey contextKey = "logger"
|
||||
requestIDKey contextKey = "request_id"
|
||||
correlationIDKey contextKey = "correlation_id"
|
||||
serviceNameKey contextKey = "service_name"
|
||||
)
|
||||
|
||||
// ContextWithLogger creates a new context with the logger
|
||||
func ContextWithLogger(ctx context.Context, logger *Logger) context.Context {
|
||||
return context.WithValue(ctx, loggerKey, logger)
|
||||
}
|
||||
|
||||
// LoggerFromContext retrieves the logger from context
|
||||
func LoggerFromContext(ctx context.Context) *Logger {
|
||||
if logger, ok := ctx.Value(loggerKey).(*Logger); ok {
|
||||
return logger
|
||||
}
|
||||
return globalLogger
|
||||
}
|
||||
|
||||
// ContextWithRequestID creates a new context with the request ID
|
||||
func ContextWithRequestID(ctx context.Context, requestID string) context.Context {
|
||||
return context.WithValue(ctx, requestIDKey, requestID)
|
||||
}
|
||||
|
||||
// RequestIDFromContext retrieves the request ID from context
|
||||
func RequestIDFromContext(ctx context.Context) string {
|
||||
if requestID, ok := ctx.Value(requestIDKey).(string); ok {
|
||||
return requestID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ContextWithCorrelationID creates a new context with the correlation ID
|
||||
func ContextWithCorrelationID(ctx context.Context, correlationID string) context.Context {
|
||||
return context.WithValue(ctx, correlationIDKey, correlationID)
|
||||
}
|
||||
|
||||
// CorrelationIDFromContext retrieves the correlation ID from context
|
||||
func CorrelationIDFromContext(ctx context.Context) string {
|
||||
if correlationID, ok := ctx.Value(correlationIDKey).(string); ok {
|
||||
return correlationID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ContextWithServiceName creates a new context with the service name
|
||||
func ContextWithServiceName(ctx context.Context, serviceName string) context.Context {
|
||||
return context.WithValue(ctx, serviceNameKey, serviceName)
|
||||
}
|
||||
|
||||
// ServiceNameFromContext retrieves the service name from context
|
||||
func ServiceNameFromContext(ctx context.Context) string {
|
||||
if serviceName, ok := ctx.Value(serviceNameKey).(string); ok {
|
||||
return serviceName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// WithContext returns a new logger with context values
|
||||
func (l *Logger) WithContext(ctx context.Context) *Logger {
|
||||
logger := l
|
||||
|
||||
if requestID := RequestIDFromContext(ctx); requestID != "" {
|
||||
logger = logger.WithRequestID(requestID)
|
||||
}
|
||||
|
||||
if correlationID := CorrelationIDFromContext(ctx); correlationID != "" {
|
||||
logger = logger.WithCorrelationID(correlationID)
|
||||
}
|
||||
|
||||
if serviceName := ServiceNameFromContext(ctx); serviceName != "" {
|
||||
logger = logger.WithService(serviceName)
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
// DebugCtx logs a debug message with context
|
||||
func DebugCtx(ctx context.Context, msg string, fields ...map[string]interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Debug(msg, fields...)
|
||||
}
|
||||
|
||||
// DebugfCtx logs a formatted debug message with context
|
||||
func DebugfCtx(ctx context.Context, format string, args ...interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Debugf(format, args...)
|
||||
}
|
||||
|
||||
// InfoCtx logs an info message with context
|
||||
func InfoCtx(ctx context.Context, msg string, fields ...map[string]interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Info(msg, fields...)
|
||||
}
|
||||
|
||||
// InfofCtx logs a formatted info message with context
|
||||
func InfofCtx(ctx context.Context, format string, args ...interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Infof(format, args...)
|
||||
}
|
||||
|
||||
// WarnCtx logs a warning message with context
|
||||
func WarnCtx(ctx context.Context, msg string, fields ...map[string]interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Warn(msg, fields...)
|
||||
}
|
||||
|
||||
// WarnfCtx logs a formatted warning message with context
|
||||
func WarnfCtx(ctx context.Context, format string, args ...interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Warnf(format, args...)
|
||||
}
|
||||
|
||||
// ErrorCtx logs an error message with context
|
||||
func ErrorCtx(ctx context.Context, msg string, fields ...map[string]interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Error(msg, fields...)
|
||||
}
|
||||
|
||||
// ErrorfCtx logs a formatted error message with context
|
||||
func ErrorfCtx(ctx context.Context, format string, args ...interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Errorf(format, args...)
|
||||
}
|
||||
|
||||
// FatalCtx logs a fatal message with context and exits the program
|
||||
func FatalCtx(ctx context.Context, msg string, fields ...map[string]interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Fatal(msg, fields...)
|
||||
}
|
||||
|
||||
// FatalfCtx logs a formatted fatal message with context and exits the program
|
||||
func FatalfCtx(ctx context.Context, format string, args ...interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).Fatalf(format, args...)
|
||||
}
|
||||
|
||||
// LogDurationCtx logs the duration of an operation with context
|
||||
func LogDurationCtx(ctx context.Context, start time.Time, operation string, fields ...map[string]interface{}) {
|
||||
LoggerFromContext(ctx).WithContext(ctx).LogDuration(start, operation, fields...)
|
||||
}
|
||||
77
pkg/logger/example_test.go
Normal file
77
pkg/logger/example_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoggerExamples(t *testing.T) {
|
||||
// Example 1: Basic logging
|
||||
Info("Application starting up")
|
||||
Debug("Debug information", map[string]interface{}{"config_loaded": true})
|
||||
|
||||
// Example 2: Service-specific logging
|
||||
authLogger := AuthServiceLogger()
|
||||
authLogger.Info("User authentication successful", map[string]interface{}{
|
||||
"user_id": "12345",
|
||||
"method": "oauth2",
|
||||
})
|
||||
|
||||
// Example 3: Error logging with context
|
||||
Error("Database connection failed", map[string]interface{}{
|
||||
"error": "connection timeout",
|
||||
"retry_count": 3,
|
||||
"max_retries": 5,
|
||||
"service": "database-service",
|
||||
})
|
||||
|
||||
// Example 4: Performance timing
|
||||
start := time.Now()
|
||||
time.Sleep(10 * time.Millisecond) // Simulate work
|
||||
globalLogger.LogDuration(start, "Database query completed", map[string]interface{}{
|
||||
"query": "SELECT * FROM users",
|
||||
"rows": 150,
|
||||
"database": "postgres",
|
||||
})
|
||||
|
||||
// Example 5: JSON format logging
|
||||
jsonLogger := New("test-service", DEBUG, true)
|
||||
jsonLogger.Info("JSON formatted log", map[string]interface{}{
|
||||
"user": map[string]interface{}{
|
||||
"id": "user-123",
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
},
|
||||
"request": map[string]interface{}{
|
||||
"method": "GET",
|
||||
"path": "/api/v1/users",
|
||||
},
|
||||
})
|
||||
|
||||
t.Log("Logger examples executed successfully")
|
||||
}
|
||||
|
||||
func TestLoggerLevels(t *testing.T) {
|
||||
// Test different log levels
|
||||
Debug("This is a debug message")
|
||||
Info("This is an info message")
|
||||
Warn("This is a warning message")
|
||||
Error("This is an error message")
|
||||
|
||||
t.Log("All log levels tested")
|
||||
}
|
||||
|
||||
func TestLoggerWithRequestContext(t *testing.T) {
|
||||
// Simulate request context with IDs
|
||||
logger := Default().
|
||||
WithRequestID("req-123456").
|
||||
WithCorrelationID("corr-789012")
|
||||
|
||||
logger.Info("Request processing started", map[string]interface{}{
|
||||
"endpoint": "/api/v1/data",
|
||||
"method": "POST",
|
||||
"client_ip": "192.168.1.100",
|
||||
})
|
||||
|
||||
t.Log("Request context logging tested")
|
||||
}
|
||||
378
pkg/logger/logger.go
Normal file
378
pkg/logger/logger.go
Normal file
@@ -0,0 +1,378 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogLevel represents the severity level of a log message
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
DEBUG LogLevel = iota
|
||||
INFO
|
||||
WARN
|
||||
ERROR
|
||||
FATAL
|
||||
)
|
||||
|
||||
var (
|
||||
levelStrings = map[LogLevel]string{
|
||||
DEBUG: "DEBUG",
|
||||
INFO: "INFO",
|
||||
WARN: "WARN",
|
||||
ERROR: "ERROR",
|
||||
FATAL: "FATAL",
|
||||
}
|
||||
|
||||
stringLevels = map[string]LogLevel{
|
||||
"DEBUG": DEBUG,
|
||||
"INFO": INFO,
|
||||
"WARN": WARN,
|
||||
"ERROR": ERROR,
|
||||
"FATAL": FATAL,
|
||||
}
|
||||
)
|
||||
|
||||
// Logger represents a structured logger instance
|
||||
type Logger struct {
|
||||
serviceName string
|
||||
level LogLevel
|
||||
output *log.Logger
|
||||
mu sync.Mutex
|
||||
jsonFormat bool
|
||||
}
|
||||
|
||||
// LogEntry represents a structured log entry
|
||||
type LogEntry struct {
|
||||
Timestamp string `json:"timestamp"`
|
||||
Level string `json:"level"`
|
||||
Service string `json:"service"`
|
||||
Message string `json:"message"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
CorrelationID string `json:"correlation_id,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
Line int `json:"line,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Fields map[string]interface{} `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// New creates a new logger instance
|
||||
func New(serviceName string, level LogLevel, jsonFormat bool) *Logger {
|
||||
return &Logger{
|
||||
serviceName: serviceName,
|
||||
level: level,
|
||||
output: log.New(os.Stdout, "", 0),
|
||||
jsonFormat: jsonFormat,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromConfig creates a new logger from configuration
|
||||
func NewFromConfig(cfg Config) *Logger {
|
||||
level := INFO
|
||||
if l, exists := stringLevels[strings.ToUpper(cfg.Level)]; exists {
|
||||
level = l
|
||||
}
|
||||
|
||||
return New(cfg.Service, level, cfg.JSONFormat)
|
||||
}
|
||||
|
||||
// Default creates a default logger instance
|
||||
func Default() *Logger {
|
||||
return New("api-service", INFO, false)
|
||||
}
|
||||
|
||||
// WithService returns a new logger with the specified service name
|
||||
func (l *Logger) WithService(serviceName string) *Logger {
|
||||
return &Logger{
|
||||
serviceName: serviceName,
|
||||
level: l.level,
|
||||
output: l.output,
|
||||
jsonFormat: l.jsonFormat,
|
||||
}
|
||||
}
|
||||
|
||||
// SetLevel sets the log level for the logger
|
||||
func (l *Logger) SetLevel(level LogLevel) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.level = level
|
||||
}
|
||||
|
||||
// SetJSONFormat sets whether to output logs in JSON format
|
||||
func (l *Logger) SetJSONFormat(jsonFormat bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.jsonFormat = jsonFormat
|
||||
}
|
||||
|
||||
// Debug logs a debug message
|
||||
func (l *Logger) Debug(msg string, fields ...map[string]interface{}) {
|
||||
l.log(DEBUG, msg, nil, fields...)
|
||||
}
|
||||
|
||||
// Debugf logs a formatted debug message
|
||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||
l.log(DEBUG, fmt.Sprintf(format, args...), nil)
|
||||
}
|
||||
|
||||
// Info logs an info message
|
||||
func (l *Logger) Info(msg string, fields ...map[string]interface{}) {
|
||||
l.log(INFO, msg, nil, fields...)
|
||||
}
|
||||
|
||||
// Infof logs a formatted info message
|
||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||
l.log(INFO, fmt.Sprintf(format, args...), nil)
|
||||
}
|
||||
|
||||
// Warn logs a warning message
|
||||
func (l *Logger) Warn(msg string, fields ...map[string]interface{}) {
|
||||
l.log(WARN, msg, nil, fields...)
|
||||
}
|
||||
|
||||
// Warnf logs a formatted warning message
|
||||
func (l *Logger) Warnf(format string, args ...interface{}) {
|
||||
l.log(WARN, fmt.Sprintf(format, args...), nil)
|
||||
}
|
||||
|
||||
// Error logs an error message
|
||||
func (l *Logger) Error(msg string, fields ...map[string]interface{}) {
|
||||
l.log(ERROR, msg, nil, fields...)
|
||||
}
|
||||
|
||||
// Errorf logs a formatted error message
|
||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||
l.log(ERROR, fmt.Sprintf(format, args...), nil)
|
||||
}
|
||||
|
||||
// Fatal logs a fatal message and exits the program
|
||||
func (l *Logger) Fatal(msg string, fields ...map[string]interface{}) {
|
||||
l.log(FATAL, msg, nil, fields...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf logs a formatted fatal message and exits the program
|
||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||
l.log(FATAL, fmt.Sprintf(format, args...), nil)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// WithRequestID returns a new logger with the specified request ID
|
||||
func (l *Logger) WithRequestID(requestID string) *Logger {
|
||||
return l.withField("request_id", requestID)
|
||||
}
|
||||
|
||||
// WithCorrelationID returns a new logger with the specified correlation ID
|
||||
func (l *Logger) WithCorrelationID(correlationID string) *Logger {
|
||||
return l.withField("correlation_id", correlationID)
|
||||
}
|
||||
|
||||
// WithField returns a new logger with an additional field
|
||||
func (l *Logger) WithField(key string, value interface{}) *Logger {
|
||||
return l.withField(key, value)
|
||||
}
|
||||
|
||||
// WithFields returns a new logger with additional fields
|
||||
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
|
||||
return &Logger{
|
||||
serviceName: l.serviceName,
|
||||
level: l.level,
|
||||
output: l.output,
|
||||
jsonFormat: l.jsonFormat,
|
||||
}
|
||||
}
|
||||
|
||||
// LogDuration logs the duration of an operation
|
||||
func (l *Logger) LogDuration(start time.Time, operation string, fields ...map[string]interface{}) {
|
||||
duration := time.Since(start)
|
||||
l.Info(fmt.Sprintf("%s completed", operation), append(fields, map[string]interface{}{
|
||||
"duration": duration.String(),
|
||||
"duration_ms": duration.Milliseconds(),
|
||||
})...)
|
||||
}
|
||||
|
||||
// log is the internal logging method
|
||||
func (l *Logger) log(level LogLevel, msg string, duration *time.Duration, fields ...map[string]interface{}) {
|
||||
if level < l.level {
|
||||
return
|
||||
}
|
||||
|
||||
// Get caller information
|
||||
_, file, line, ok := runtime.Caller(3) // Adjust caller depth
|
||||
var callerFile string
|
||||
var callerLine int
|
||||
if ok {
|
||||
// Shorten file path
|
||||
parts := strings.Split(file, "/")
|
||||
if len(parts) > 2 {
|
||||
callerFile = strings.Join(parts[len(parts)-2:], "/")
|
||||
} else {
|
||||
callerFile = file
|
||||
}
|
||||
callerLine = line
|
||||
}
|
||||
|
||||
// Merge all fields
|
||||
mergedFields := make(map[string]interface{})
|
||||
for _, f := range fields {
|
||||
for k, v := range f {
|
||||
mergedFields[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
entry := LogEntry{
|
||||
Timestamp: time.Now().Format(time.RFC3339),
|
||||
Level: levelStrings[level],
|
||||
Service: l.serviceName,
|
||||
Message: msg,
|
||||
File: callerFile,
|
||||
Line: callerLine,
|
||||
Fields: mergedFields,
|
||||
}
|
||||
|
||||
if duration != nil {
|
||||
entry.Duration = duration.String()
|
||||
}
|
||||
|
||||
if l.jsonFormat {
|
||||
l.outputJSON(entry)
|
||||
} else {
|
||||
l.outputText(entry)
|
||||
}
|
||||
|
||||
if level == FATAL {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// outputJSON outputs the log entry in JSON format
|
||||
func (l *Logger) outputJSON(entry LogEntry) {
|
||||
jsonData, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
// Fallback to text output if JSON marshaling fails
|
||||
l.outputText(entry)
|
||||
return
|
||||
}
|
||||
l.output.Println(string(jsonData))
|
||||
}
|
||||
|
||||
// outputText outputs the log entry in text format
|
||||
func (l *Logger) outputText(entry LogEntry) {
|
||||
timestamp := entry.Timestamp
|
||||
level := entry.Level
|
||||
service := entry.Service
|
||||
message := entry.Message
|
||||
|
||||
// Base log line
|
||||
logLine := fmt.Sprintf("%s [%s] %s: %s", timestamp, level, service, message)
|
||||
|
||||
// Add file and line if available
|
||||
if entry.File != "" && entry.Line > 0 {
|
||||
logLine += fmt.Sprintf(" (%s:%d)", entry.File, entry.Line)
|
||||
}
|
||||
|
||||
// Add request ID if available
|
||||
if entry.RequestID != "" {
|
||||
logLine += fmt.Sprintf(" [req:%s]", entry.RequestID)
|
||||
}
|
||||
|
||||
// Add correlation ID if available
|
||||
if entry.CorrelationID != "" {
|
||||
logLine += fmt.Sprintf(" [corr:%s]", entry.CorrelationID)
|
||||
}
|
||||
|
||||
// Add duration if available
|
||||
if entry.Duration != "" {
|
||||
logLine += fmt.Sprintf(" [dur:%s]", entry.Duration)
|
||||
}
|
||||
|
||||
// Add additional fields
|
||||
if len(entry.Fields) > 0 {
|
||||
fields := make([]string, 0, len(entry.Fields))
|
||||
for k, v := range entry.Fields {
|
||||
fields = append(fields, fmt.Sprintf("%s=%v", k, v))
|
||||
}
|
||||
logLine += " [" + strings.Join(fields, " ") + "]"
|
||||
}
|
||||
|
||||
l.output.Println(logLine)
|
||||
}
|
||||
|
||||
// withField creates a new logger with an additional field
|
||||
func (l *Logger) withField(key string, value interface{}) *Logger {
|
||||
return &Logger{
|
||||
serviceName: l.serviceName,
|
||||
level: l.level,
|
||||
output: l.output,
|
||||
jsonFormat: l.jsonFormat,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of a log level
|
||||
func (l LogLevel) String() string {
|
||||
return levelStrings[l]
|
||||
}
|
||||
|
||||
// ParseLevel parses a string into a LogLevel
|
||||
func ParseLevel(level string) (LogLevel, error) {
|
||||
if l, exists := stringLevels[strings.ToUpper(level)]; exists {
|
||||
return l, nil
|
||||
}
|
||||
return INFO, fmt.Errorf("invalid log level: %s", level)
|
||||
}
|
||||
|
||||
// Global logger instance
|
||||
var globalLogger = Default()
|
||||
|
||||
// SetGlobalLogger sets the global logger instance
|
||||
func SetGlobalLogger(logger *Logger) {
|
||||
globalLogger = logger
|
||||
}
|
||||
|
||||
// Global logging functions
|
||||
func Debug(msg string, fields ...map[string]interface{}) {
|
||||
globalLogger.Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
globalLogger.Debugf(format, args...)
|
||||
}
|
||||
|
||||
func Info(msg string, fields ...map[string]interface{}) {
|
||||
globalLogger.Info(msg, fields...)
|
||||
}
|
||||
|
||||
func Infof(format string, args ...interface{}) {
|
||||
globalLogger.Infof(format, args...)
|
||||
}
|
||||
|
||||
func Warn(msg string, fields ...map[string]interface{}) {
|
||||
globalLogger.Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
globalLogger.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func Error(msg string, fields ...map[string]interface{}) {
|
||||
globalLogger.Error(msg, fields...)
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
globalLogger.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, fields ...map[string]interface{}) {
|
||||
globalLogger.Fatal(msg, fields...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
globalLogger.Fatalf(format, args...)
|
||||
}
|
||||
191
pkg/logger/middleware.go
Normal file
191
pkg/logger/middleware.go
Normal file
@@ -0,0 +1,191 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user