# 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.