Gin Logger Middleware
When building web applications, logging is a crucial aspect for debugging, monitoring, and understanding what happens in your application. Gin, a popular web framework for Go, provides a built-in logger middleware that helps track HTTP requests and responses with minimal configuration.
Introduction to Gin Logger
Logger middleware in Gin records information about each incoming HTTP request, including:
- Request timestamp
- HTTP method
- Request path
- Response status code
- Response time
- Client IP address
This information is valuable for monitoring your application's performance, troubleshooting issues, and understanding usage patterns.
Basic Usage
To use Gin's logger middleware, you simply need to include it when setting up your router. Here's a basic example:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// Create a default Gin router with Logger and Recovery middleware
router := gin.Default()
// Add a simple route
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
// Start the server
router.Run(":8080")
}
When you run this application and make a request to /ping
, you'll see output in your console similar to:
[GIN] 2023/05/15 - 12:34:56 | 200 | 123.456µs | 127.0.0.1 | GET "/ping"
This log line shows:
- Timestamp:
2023/05/15 - 12:34:56
- Status code:
200
- Latency:
123.456µs
- Client IP:
127.0.0.1
- HTTP method:
GET
- Path:
"/ping"
Manual Logger Configuration
If you want more control over your logging setup, you can explicitly add the logger middleware rather than using gin.Default()
:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// Create a Gin router with no middleware by default
router := gin.New()
// Add the Logger middleware
router.Use(gin.Logger())
// Add the Recovery middleware
router.Use(gin.Recovery())
// Add a simple route
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
// Start the server
router.Run(":8080")
}
The output will be the same as with gin.Default()
, but this approach gives you flexibility to customize your middleware stack.
Customizing the Logger
Gin allows you to customize the logger format to suit your needs. Here's how you can create a custom formatter:
package main
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
func main() {
router := gin.New()
// Customize the logger output format
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// Custom format
return fmt.Sprintf("[GIN] %s | %s | %d | %s | %s | %s\n",
param.TimeStamp.Format("2006-01-02 15:04:05"),
param.ClientIP,
param.StatusCode,
param.Latency,
param.Method,
param.Path,
)
}))
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
router.Run(":8080")
}
With this customization, your logs will look like:
[GIN] 2023-05-15 12:34:56 | 127.0.0.1 | 200 | 123.456µs | GET | /ping
Logging to a File
By default, Gin logs to the console (stdout
), but in production environments, you'll often want to log to a file. Here's how you can redirect the log output:
package main
import (
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
// Create a log file
logFile, _ := os.Create("gin.log")
// Use io.MultiWriter to write logs to both file and console
gin.DefaultWriter = io.MultiWriter(logFile, os.Stdout)
// Create a router with default middleware
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
router.Run(":8080")
}
With this setup, logs will be written to both the console and the gin.log
file.
Color-coded Logs for Development
Gin's logger uses colored output by default to highlight different status codes:
- Green for successful responses (2xx)
- Blue for redirection responses (3xx)
- Yellow for client errors (4xx)
- Red for server errors (5xx)
This makes it easier to spot issues during development. However, you may want to disable colors for production logging:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// Disable colored logs
gin.DisableConsoleColor()
// Or, to re-enable colors:
// gin.ForceConsoleColor()
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
router.Run(":8080")
}
Real-World Example: API Request Logging
Here's a practical example showing how to implement a more comprehensive logging system for a REST API:
package main
import (
"github.com/gin-gonic/gin"
"log"
"time"
"os"
)
// Logger middleware that logs request details and timing information
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
// Start time
startTime := time.Now()
// Log request details
log.Printf("Request: %s %s", c.Request.Method, c.Request.URL.Path)
// Process request
c.Next()
// End time
endTime := time.Now()
// Execution time
latency := endTime.Sub(startTime)
// Log response details
log.Printf("Response: %s %s | %d | %s",
c.Request.Method,
c.Request.URL.Path,
c.Writer.Status(),
latency,
)
}
}
func main() {
// Set log format
log.SetFlags(log.Ldate | log.Ltime)
// Create log file
logFile, err := os.OpenFile("api.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err == nil {
log.SetOutput(logFile)
defer logFile.Close()
}
// Initialize Gin
router := gin.New()
// Use our custom logger
router.Use(Logger())
// Use recovery middleware
router.Use(gin.Recovery())
// Define some API routes
router.GET("/api/users", func(c *gin.Context) {
// Simulate processing time
time.Sleep(100 * time.Millisecond)
c.JSON(200, gin.H{"users": []string{"user1", "user2"}})
})
router.GET("/api/products", func(c *gin.Context) {
// Simulate processing time
time.Sleep(50 * time.Millisecond)
c.JSON(200, gin.H{"products": []string{"product1", "product2"}})
})
router.POST("/api/orders", func(c *gin.Context) {
// Simulate processing time
time.Sleep(200 * time.Millisecond)
c.JSON(201, gin.H{"message": "Order created"})
})
// Start server
router.Run(":8080")
}
This example implements a custom logger that captures both request and response details, writing them to a dedicated API log file.
Using Structured Logging
For more advanced applications, structured logging is often better than simple text logs. You can integrate structured logging libraries like logrus
with Gin:
package main
import (
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"os"
"time"
)
func main() {
// Configure logrus
log := logrus.New()
log.SetFormatter(&logrus.JSONFormatter{})
log.SetOutput(os.Stdout)
// Create a Gin router
router := gin.New()
// Use a custom middleware for structured logging
router.Use(func(c *gin.Context) {
// Start timer
startTime := time.Now()
// Process request
c.Next()
// Log details
log.WithFields(logrus.Fields{
"status": c.Writer.Status(),
"method": c.Request.Method,
"path": c.Request.URL.Path,
"ip": c.ClientIP(),
"latency": time.Since(startTime).String(),
"user_agent": c.Request.UserAgent(),
}).Info("Request processed")
})
// Recovery middleware
router.Use(gin.Recovery())
// Routes
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
// Start server
router.Run(":8080")
}
With this setup, you'll get structured JSON logs like:
{
"ip": "127.0.0.1",
"latency": "125.725µs",
"level": "info",
"method": "GET",
"msg": "Request processed",
"path": "/ping",
"status": 200,
"time": "2023-05-15T12:34:56Z",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
Summary
The Gin Logger middleware provides essential request logging capabilities for your web applications. Key takeaways include:
- Gin's default logger provides a good starting point for basic applications
- You can customize the log format to suit your needs
- For production use, consider logging to files
- Custom logging middleware can be created for more advanced requirements
- Structured logging can make logs more machine-readable and analyzable
Implementing proper logging is one of the foundations of building robust web applications, and Gin makes it easy to start with its built-in middleware.
Exercises
- Create a Gin application that logs requests to a file named
requests.log
and errors to a separate file namederrors.log
. - Implement a custom logger that adds the request query parameters to the log output.
- Build a middleware that logs the size of the response in bytes.
- Create a logging middleware that skips logging for certain paths (like
/health
or/metrics
). - Implement a logger that rotates log files daily, keeping logs organized by date.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)