Skip to main content

Echo Logger Middleware

When building web applications, logging is crucial for debugging, monitoring, and tracking application behavior. Echo's Logger middleware provides a built-in solution for logging HTTP requests and responses with minimal configuration. This guide will help you understand how to implement, customize, and use the Logger middleware effectively in your Echo applications.

Introduction

The Logger middleware logs information about each HTTP request and response that passes through your Echo application. It captures details like:

  • HTTP method
  • Request URL
  • Status code
  • Latency (response time)
  • Request and response size
  • Client IP address

By default, the Logger middleware is already included when you create a new Echo instance with echo.New(). However, understanding how to customize and configure it will help you get the most value from your application logs.

Basic Usage

Let's start with a simple example of the Logger middleware in action:

go
package main

import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
)

func main() {
// Create a new Echo instance
e := echo.New()

// Add Logger middleware
e.Use(middleware.Logger())

// Define a simple route
e.GET("/hello", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})

// Start the server
e.Start(":8080")
}

When you run this application and make a request to /hello, you'll see log output similar to this:

⇨ http server started on [::]:8080
{"time":"2023-07-10T12:34:56.789Z","id":"","remote_ip":"127.0.0.1","host":"localhost:8080","method":"GET","uri":"/hello","user_agent":"curl/7.68.0","status":200,"error":"","latency":0.000234,"latency_human":"234µs","bytes_in":0,"bytes_out":13}

Customizing the Logger

Echo's Logger middleware is highly customizable. You can change the output format, specify which fields to include, and define where logs should be written.

Custom Format

You can customize the log format using the Format option:

go
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "time=${time_rfc3339}, method=${method}, uri=${uri}, status=${status}\n",
}))

This will produce logs like:

time=2023-07-10T12:34:56.789Z, method=GET, uri=/hello, status=200

Available Format Fields

Here are some of the fields you can use in your custom format string:

  • ${time_rfc3339}: RFC3339 formatted time
  • ${remote_ip}: Client IP address
  • ${host}: Host header
  • ${method}: HTTP method
  • ${uri}: Request URI
  • ${path}: Request path
  • ${status}: Response status
  • ${latency}: Response time in seconds
  • ${latency_human}: Human-readable response time
  • ${bytes_in}: Request size in bytes
  • ${bytes_out}: Response size in bytes
  • ${user_agent}: User agent header
  • ${error}: Error message

Custom Output

By default, the Logger middleware writes to os.Stdout, but you can change this by providing a custom output writer:

go
// Log to a file
logFile, err := os.OpenFile("app.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
e.Logger.Fatal(err)
}

// Configure the logger middleware
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Output: logFile,
}))

Advanced Configuration

Let's explore some advanced configurations for the Logger middleware.

Selective Logging

You might want to exclude certain routes from being logged. Use the Skipper function to determine which requests to skip:

go
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Skipper: func(c echo.Context) bool {
// Skip logging for health check endpoints
return c.Path() == "/health" || c.Path() == "/metrics"
},
}))

Custom Log Function

For complete control over logging behavior, you can provide a custom log function:

go
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "${method} ${uri} ${status} ${latency_human}\n",
CustomTimeFormat: "2006-01-02 15:04:05.00000",
Output: logFile,
}))

Real-World Application: API Request Logging

Here's a more complete example showing how to implement logging in a real-world API:

go
package main

import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
"os"
)

func main() {
// Create a new Echo instance
e := echo.New()

// Create log file
logFile, err := os.OpenFile("api.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
e.Logger.Fatal(err)
}

// Configure middleware
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}",` +
`"method":"${method}","uri":"${uri}","status":${status},` +
`"latency":${latency},"latency_human":"${latency_human}",` +
`"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n",
Output: logFile,
Skipper: func(c echo.Context) bool {
return c.Path() == "/health"
},
}))

// Define routes
e.GET("/health", func(c echo.Context) error {
return c.String(http.StatusOK, "OK")
})

e.GET("/users/:id", func(c echo.Context) error {
id := c.Param("id")
return c.JSON(http.StatusOK, map[string]string{"user_id": id, "name": "John Doe"})
})

e.POST("/users", func(c echo.Context) error {
return c.JSON(http.StatusCreated, map[string]string{"id": "123", "status": "created"})
})

// Start the server
e.Logger.Fatal(e.Start(":8080"))
}

This setup will:

  • Log detailed information about all API requests except for /health checks
  • Write logs in JSON format for easier parsing
  • Include detailed timing information for performance monitoring
  • Store logs in a dedicated file (api.log)

Integrating with External Logging Libraries

Echo's Logger middleware works well on its own, but you can also integrate it with external logging libraries like logrus or zap for more advanced features.

Example with Logrus

go
package main

import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/sirupsen/logrus"
"io"
"net/http"
"os"
)

type LogrusLogger struct {
*logrus.Logger
}

func (l LogrusLogger) Write(p []byte) (n int, err error) {
l.Logger.Info(string(p))
return len(p), nil
}

func main() {
e := echo.New()

// Configure logrus
log := logrus.New()
log.SetFormatter(&logrus.JSONFormatter{})
log.SetOutput(os.Stdout)

// Use logrus as the output for Echo's Logger middleware
logrusOutput := LogrusLogger{log}

e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Output: io.MultiWriter(os.Stdout, logrusOutput),
}))

e.GET("/hello", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})

e.Start(":8080")
}

Summary

The Echo Logger middleware is a powerful tool for tracking and monitoring HTTP requests in your web applications. It provides:

  • Built-in request logging with minimal setup
  • Extensive customization options for log format and output
  • Ability to skip logging for specific routes
  • Easy integration with external logging libraries

Proper logging is essential for debugging issues, monitoring performance, and understanding how your application is being used. With Echo's Logger middleware, you can easily implement comprehensive logging to meet these needs.

Additional Resources

Exercises

  1. Implement Logger middleware that excludes logging for requests with certain query parameters.
  2. Create a custom logger that records additional information like request headers or user IDs from authentication tokens.
  3. Set up a logging system that writes different types of requests (e.g., errors vs. successful requests) to different log files.
  4. Integrate Echo's Logger middleware with a structured logging library like zap or zerolog.
  5. Build a middleware chain that combines logging with request timing and error handling for comprehensive request monitoring.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)