Echo Logging
Logging is an essential aspect of any web application, providing visibility into what's happening in your system during runtime. Echo provides a powerful and flexible logging system that helps developers track application events, debug issues, and monitor performance. In this guide, we'll explore Echo's logging features and how to use them effectively in your projects.
Introduction to Echo Logging
Echo's logging system allows you to record events, errors, and information about the application's state. The framework provides a default logger but also allows you to integrate custom loggers to fit your specific needs.
Logging in Echo serves several important purposes:
- Debugging application issues
- Tracking user activity
- Monitoring application performance
- Recording errors and exceptions
- Providing an audit trail of system events
Basic Logging with Echo
The Logger Interface
Echo defines a simple Logger
interface that any logging implementation must satisfy:
type Logger interface {
Debug(...interface{})
Debugf(string, ...interface{})
Info(...interface{})
Infof(string, ...interface{})
Warn(...interface{})
Warnf(string, ...interface{})
Error(...interface{})
Errorf(string, ...interface{})
}
Default Logger
Echo comes with a default logger that writes to standard output. Here's a basic example of how to use it:
package main
import (
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
// Using the default logger
e.Logger.Info("Server starting up")
e.Logger.Debugf("Debug mode: %v", e.Debug)
// Handle requests
e.GET("/", func(c echo.Context) error {
e.Logger.Info("Handling root request")
return c.String(200, "Hello, World!")
})
e.Logger.Fatal(e.Start(":8080"))
}
The output will look something like:
{"time":"2023-07-20T10:15:30Z","level":"INFO","prefix":"echo","message":"Server starting up"}
{"time":"2023-07-20T10:15:30Z","level":"DEBUG","prefix":"echo","message":"Debug mode: false"}
Log Levels
Echo's logger supports different log levels, allowing you to control the verbosity of your logs:
Debug
: Detailed information for debuggingInfo
: Normal operational informationWarn
: Warning events that might need attentionError
: Error events that might still allow the application to continueFatal
: Critical errors that stop the application
Customizing the Logger
Setting Log Level
You can control which logs are displayed by setting the log level:
e := echo.New()
// Set the logger to only show info, warn, error, and fatal logs
e.Logger.SetLevel(log.INFO)
Custom Logger Implementation
Echo allows you to replace the default logger with your own implementation. This is useful when you want to integrate with logging frameworks like Zap, Logrus, or others.
Here's an example of integrating Logrus with Echo:
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"github.com/sirupsen/logrus"
)
// LogrusLogger adapts logrus to Echo's logger interface
type LogrusLogger struct {
*logrus.Logger
}
func (l LogrusLogger) Debug(i ...interface{}) {
l.Logger.Debug(i...)
}
func (l LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...)
}
func (l LogrusLogger) Info(i ...interface{}) {
l.Logger.Info(i...)
}
func (l LogrusLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...)
}
func (l LogrusLogger) Warn(i ...interface{}) {
l.Logger.Warn(i...)
}
func (l LogrusLogger) Warnf(format string, args ...interface{}) {
l.Logger.Warnf(format, args...)
}
func (l LogrusLogger) Error(i ...interface{}) {
l.Logger.Error(i...)
}
func (l LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Errorf(format, args...)
}
func (l LogrusLogger) SetLevel(level log.Lvl) {
switch level {
case log.DEBUG:
l.Logger.SetLevel(logrus.DebugLevel)
case log.INFO:
l.Logger.SetLevel(logrus.InfoLevel)
case log.WARN:
l.Logger.SetLevel(logrus.WarnLevel)
case log.ERROR:
l.Logger.SetLevel(logrus.ErrorLevel)
}
}
func (l LogrusLogger) Level() log.Lvl {
switch l.Logger.GetLevel() {
case logrus.DebugLevel:
return log.DEBUG
case logrus.InfoLevel:
return log.INFO
case logrus.WarnLevel:
return log.WARN
case logrus.ErrorLevel:
return log.ERROR
default:
return log.OFF
}
}
func main() {
e := echo.New()
// Create a new logrus logger
logrusLogger := logrus.New()
logrusLogger.SetFormatter(&logrus.JSONFormatter{})
// Set logrus as Echo's logger
e.Logger = LogrusLogger{Logger: logrusLogger}
e.Logger.SetLevel(log.INFO)
e.Logger.Info("Server starting with custom logger")
// Routes
e.GET("/", func(c echo.Context) error {
e.Logger.Info("Handling root request")
return c.String(200, "Hello, World!")
})
e.Logger.Fatal(e.Start(":8080"))
}
HTTP Request Logging Middleware
Echo provides middleware for logging HTTP requests, which is essential for tracking API usage, performance, and troubleshooting issues.
Basic Request Logging
The simplest way to enable request logging is to use Echo's built-in middleware:
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
// Add logger middleware
e.Use(middleware.Logger())
// Routes
e.GET("/", func(c echo.Context) error {
return c.String(200, "Hello, World!")
})
e.Logger.Fatal(e.Start(":8080"))
}
When you make a request to http://localhost:8080/
, you'll see a log entry like this:
{"time":"2023-07-20T10:30:45.1234Z","id":"","remote_ip":"127.0.0.1","host":"localhost:8080","method":"GET","uri":"/","user_agent":"Mozilla/5.0","status":200,"error":"","latency":0.001,"latency_human":"1ms","bytes_in":0,"bytes_out":12}
Customizing Request Logs
You can configure the logger middleware to customize what information is logged and how it's formatted:
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "method=${method}, uri=${uri}, status=${status}\n",
}))
With this configuration, logs will appear as:
method=GET, uri=/, status=200
Common Log Formats
Echo supports various log formats, including predefined ones like:
// Apache Common Log Format
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "${remote_ip} - - [${time_rfc3339}] \"${method} ${uri} ${protocol}\" ${status} ${bytes_out}\n",
}))
This will output logs like:
127.0.0.1 - - [2023-07-20T10:30:45Z] "GET / HTTP/1.1" 200 12
Advanced Logging Techniques
Contextual Logging
You can add context-specific information to your logs by using Echo's context:
e.GET("/users/:id", func(c echo.Context) error {
userID := c.Param("id")
e.Logger.Infof("Processing request for user ID: %s", userID)
// Simulate user lookup
if userID == "1" {
e.Logger.Info("User found")
return c.String(200, "User details for ID 1")
}
e.Logger.Warn("User not found")
return c.String(404, "User not found")
})
Structured Logging
Structured logging makes it easier to parse and analyze logs. Here's how to do structured logging with a custom logger like zerolog:
package main
import (
"os"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog"
)
type ZeroLogger struct {
logger zerolog.Logger
}
// Implement Echo's Logger interface with zerolog
// (implementation details similar to the Logrus example above)
func main() {
e := echo.New()
// Create zerolog logger
zl := zerolog.New(os.Stdout).With().Timestamp().Logger()
// Set as Echo's logger
e.Logger = &ZeroLogger{logger: zl}
// Example of structured logging
e.GET("/order/:id", func(c echo.Context) error {
orderID := c.Param("id")
// Log with context fields
e.Logger.Info().
Str("order_id", orderID).
Str("user_ip", c.RealIP()).
Msg("Processing order")
return c.String(200, "Order processed")
})
e.Logger.Fatal(e.Start(":8080"))
}
Practical Example: Complete Logging Setup
Here's a more complete example that shows how to set up logging for a production application:
package main
import (
"os"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog"
"time"
)
// ZeroLogger adapts zerolog to Echo's logger interface
type ZeroLogger struct {
logger zerolog.Logger
}
// Implement all required Logger interface methods
func main() {
// Initialize Echo
e := echo.New()
// Set up zerolog
output := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: time.RFC3339,
NoColor: false,
}
logger := zerolog.New(output).
Level(zerolog.InfoLevel).
With().
Timestamp().
Str("service", "api-server").
Logger()
// Set custom logger
e.Logger = &ZeroLogger{logger: logger}
// Configure HTTP request logging middleware
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "${time_rfc3339} ${remote_ip} ${method} ${uri} ${protocol} ${status} ${latency_human} ${bytes_in} ${bytes_out}\n",
CustomTimeFormat: time.RFC3339,
}))
// Add recovery middleware to log panics
e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
LogLevel: 1, // ERROR level
LogErrorFunc: func(c echo.Context, err error, stack []byte) error {
e.Logger.Errorf("Panic recovered: %v\n%s", err, stack)
return err
},
}))
// Routes
e.GET("/", func(c echo.Context) error {
e.Logger.Info("Handling home request")
return c.String(200, "Welcome to the API")
})
e.GET("/error", func(c echo.Context) error {
e.Logger.Error("This is a simulated error")
return c.String(500, "Internal Server Error")
})
e.GET("/panic", func(c echo.Context) error {
panic("This is a simulated panic")
})
// Start server
e.Logger.Info("Starting server on port 8080")
e.Logger.Fatal(e.Start(":8080"))
}
Logging Best Practices
-
Choose appropriate log levels - Use DEBUG for detailed troubleshooting, INFO for general application flow, WARN for potentially problematic situations, and ERROR for actual errors.
-
Include context in logs - Add relevant details such as request IDs, user IDs, and transaction IDs to make troubleshooting easier.
-
Use structured logging - Structured logs are easier to parse, filter, and analyze.
-
Log sensitive data carefully - Never log passwords, tokens, or personal information. Consider masking sensitive data.
-
Configure log rotation - For production systems, ensure logs are rotated to prevent disk space issues.
-
Consider log aggregation - In distributed systems, use tools like ELK (Elasticsearch, Logstash, Kibana) or a cloud logging service to aggregate logs from multiple instances.
Summary
Echo provides a flexible and powerful logging system that can be tailored to your application's needs. From the simple default logger to custom integrations with popular logging libraries, Echo makes it easy to implement comprehensive logging for your web applications.
Effective logging is crucial for debugging, monitoring, and maintaining web applications. By following the examples and best practices in this guide, you can set up a robust logging system that will help you keep your Echo applications running smoothly.
Additional Resources
- Echo Framework Documentation
- Zerolog GitHub Repository
- Logrus GitHub Repository
- ELK Stack Documentation
Exercises
-
Basic Logger Implementation: Create a simple Echo application that logs different types of events using the default logger.
-
Custom Logger: Implement a custom logger for Echo using a logging library not covered in this guide (like slog or zap).
-
Enhanced Request Logging: Configure the logger middleware to include additional information such as request headers and query parameters.
-
Log Rotation: Set up log rotation for an Echo application using a library like lumberjack.
-
Structured Logging: Create an application that uses structured logging with contextual information for different API endpoints.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)