Structured Logging
Structured Logging
Section titled “Structured Logging”Fox includes a production-ready structured logging system with automatic TraceID generation, contextual fields, and log rotation.
Basic Usage
Section titled “Basic Usage”Getting Logger from Context
Section titled “Getting Logger from Context”r := fox.Default()
r.GET("/user/:id", func(c *gin.Context, id int) (*User, error) { logger := fox.GetLogger(c)
logger.Info("Fetching user", "user_id", id)
user, err := getUserByID(id) if err != nil { logger.Error("Failed to fetch user", "user_id", id, "error", err) return nil, err }
logger.Info("User fetched successfully", "user_id", id) return user, nil})Automatic TraceID
Section titled “Automatic TraceID”Each request automatically gets a unique TraceID for request tracking:
r.GET("/process", func(c *gin.Context) error { logger := fox.GetLogger(c)
// TraceID is automatically included in logs logger.Info("Processing started")
// Call another service result, err := processData()
logger.Info("Processing completed", "result", result) return err})Output:
{"level":"info","trace_id":"abc123","msg":"Processing started","time":"2024-01-01T10:00:00Z"}{"level":"info","trace_id":"abc123","result":"success","msg":"Processing completed","time":"2024-01-01T10:00:01Z"}Configuration
Section titled “Configuration”Basic Configuration
Section titled “Basic Configuration”config := fox.LoggerConfig{ Level: "info", // Log level: debug, info, warn, error Output: "stdout", // Output: stdout, stderr, or file path Format: "json", // Format: json or text TimeFormat: time.RFC3339, // Timestamp format}
r := fox.New(fox.WithLoggerConfig(config))File Rotation
Section titled “File Rotation”config := fox.LoggerConfig{ Level: "info", Output: "/var/log/app.log", MaxSize: 100, // Max size in MB before rotation MaxBackups: 7, // Number of old log files to keep MaxAge: 30, // Max days to keep old log files Compress: true, // Compress rotated files}
r := fox.New(fox.WithLoggerConfig(config))Structured Fields
Section titled “Structured Fields”Adding Context Fields
Section titled “Adding Context Fields”r.POST("/order", func(c *gin.Context, req *CreateOrderRequest) (*Order, error) { logger := fox.GetLogger(c)
logger.Info("Creating order", "user_id", req.UserID, "items_count", len(req.Items), "total_amount", req.TotalAmount, )
order, err := createOrder(req)
if err != nil { logger.Error("Order creation failed", "user_id", req.UserID, "error", err, "reason", "database_error", ) return nil, err }
logger.Info("Order created successfully", "order_id", order.ID, "user_id", req.UserID, )
return order, nil})Nested Structures
Section titled “Nested Structures”logger.Info("User action", "user", map[string]any{ "id": user.ID, "email": user.Email, "role": user.Role, }, "action", "purchase", "metadata", map[string]any{ "ip": c.ClientIP(), "user_agent": c.Request.UserAgent(), },)Log Levels
Section titled “Log Levels”logger := fox.GetLogger(c)
logger.Debug("Detailed debugging information", "key", "value")logger.Info("General information", "status", "ok")logger.Warn("Warning message", "issue", "deprecated_api")logger.Error("Error occurred", "error", err)Middleware Integration
Section titled “Middleware Integration”Custom Context Fields
Section titled “Custom Context Fields”Add fields to all logs in a request:
func TenantMiddleware() gin.HandlerFunc { return func(c *gin.Context) { tenantID := extractTenantID(c)
// Add tenant_id to logger context logger := fox.GetLogger(c) logger = logger.With("tenant_id", tenantID) fox.SetLogger(c, logger)
c.Next() }}
r := fox.Default()r.Use(TenantMiddleware())Request Logging Middleware
Section titled “Request Logging Middleware”func RequestLoggerMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path
logger := fox.GetLogger(c) logger.Info("Request started", "method", c.Request.Method, "path", path, )
c.Next()
latency := time.Since(start) logger.Info("Request completed", "method", c.Request.Method, "path", path, "status", c.Writer.Status(), "latency_ms", latency.Milliseconds(), ) }}Error Tracking
Section titled “Error Tracking”Logging with Stack Traces
Section titled “Logging with Stack Traces”func handler(c *gin.Context) error { logger := fox.GetLogger(c)
err := someOperation() if err != nil { // Log with stack trace logger.Error("Operation failed", "error", err, "stack", string(debug.Stack()), ) return err }
return nil}Best Practices
Section titled “Best Practices”-
Use structured fields - Don’t concatenate strings into messages
// ❌ Badlogger.Info("User " + userID + " created order " + orderID)// ✅ Goodlogger.Info("Order created", "user_id", userID, "order_id", orderID) -
Log at appropriate levels
Debug: Detailed information for debuggingInfo: General operational informationWarn: Warning messages for potential issuesError: Error conditions that need attention
-
Include relevant context
logger.Error("Database query failed","query", sqlQuery,"params", params,"error", err,"duration_ms", duration.Milliseconds(),) -
Don’t log sensitive data - Avoid logging passwords, tokens, PII
// ❌ Badlogger.Info("Login", "password", password)// ✅ Goodlogger.Info("Login attempt", "username", username) -
Use TraceID for distributed tracing - Pass TraceID to external services
Integration with External Services
Section titled “Integration with External Services”Send Logs to External Service
Section titled “Send Logs to External Service”type ExternalLoggerHook struct { client *http.Client url string}
func (h *ExternalLoggerHook) Levels() []logrus.Level { return logrus.AllLevels}
func (h *ExternalLoggerHook) Fire(entry *logrus.Entry) error { // Send log to external service (Elasticsearch, Splunk, etc.) return h.sendLog(entry)}Correlation with Metrics
Section titled “Correlation with Metrics”r.GET("/api/data", func(c *gin.Context) (any, error) { logger := fox.GetLogger(c) traceID := fox.GetTraceID(c)
// Use TraceID in metrics tags metrics.Increment("api.requests", map[string]string{ "trace_id": traceID, "endpoint": "/api/data", })
logger.Info("API called", "endpoint", "/api/data")
return getData()})Next Steps
Section titled “Next Steps”- Parameter Binding - Automatic request binding
- Validation - Custom validation with logging