8.3 KiB
8.3 KiB
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:
import "api-service/pkg/logger"
Quick Start
Basic Usage
// 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
// 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
// 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
// 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:
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
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:
# 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
// 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
{
"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
// 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
// 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
// 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
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)
import "log"
log.Printf("Error: %v", err)
log.Printf("User %s logged in", username)
After (structured logger)
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
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
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
- No logs appearing: Check that log level is not set too high (e.g., ERROR when logging INFO)
- JSON format not working: Ensure
LOG_JSON=trueor logger is created withjsonFormat: true - Missing context: Use
WithRequestID()andWithCorrelationID()for request context
Debug Mode
Enable debug logging for development:
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.