357 lines
8.3 KiB
Markdown
357 lines
8.3 KiB
Markdown
# 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.
|