Skip to main content

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:

go
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():

go
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:

go
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:

go
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:

go
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:

go
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:

go
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:

json
{
"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​

  1. Create a Gin application that logs requests to a file named requests.log and errors to a separate file named errors.log.
  2. Implement a custom logger that adds the request query parameters to the log output.
  3. Build a middleware that logs the size of the response in bytes.
  4. Create a logging middleware that skips logging for certain paths (like /health or /metrics).
  5. 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 feedback@compilenrun.com. I’d greatly appreciate your feedback! :)