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:
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:
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:
// 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:
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:
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:
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
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
- Official Echo Logger Middleware Documentation
- Go Logging Best Practices
- JSON Logging for Machine Processing
Exercises
- Implement Logger middleware that excludes logging for requests with certain query parameters.
- Create a custom logger that records additional information like request headers or user IDs from authentication tokens.
- Set up a logging system that writes different types of requests (e.g., errors vs. successful requests) to different log files.
- Integrate Echo's Logger middleware with a structured logging library like zap or zerolog.
- 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! :)