Adding Logging and Error Handling Middleware to Your Go API
Neel Patel
Posted on October 5, 2024
Quick Note: If you checked out my previous post on JWT authentication and noticed some rendering issues, those have now been fixed! Be sure to give it another look because these examples build on top of that tutorial. :)
Alright folks, we’ve got our Go API running, we’ve added JWT authentication, and we’ve even connected it to a PostgreSQL database. But we’re not done yet! This week, we’re going to take things up a notch and make our API smarter and more developer-friendly by adding custom middleware for logging and error handling.
What’s Middleware Again? 🤔
Middleware is like a bouncer at your favorite club—it intercepts requests before they hit your API endpoints. You can have middleware that checks authentication (like we did with JWT), logs information, or handles errors when things go wrong.
Today, we’re going to build middleware that:
- Logs: Every incoming request, so we know who’s knocking on our API’s door.
- Handles Errors: Gracefully, so your users don’t see those ugly 500 errors.
Let’s dive into it!
Step 1: Creating a Logging Middleware 📝
Logging is your best friend when it comes to debugging and understanding what’s happening in your API. We’re going to create a middleware that logs every request that comes through—method, URL, and time taken.
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Log the method and the requested URL
log.Printf("Started %s %s", r.Method, r.URL.Path)
// Call the next handler in the chain
next.ServeHTTP(w, r)
// Log how long it took
log.Printf("Completed in %v", time.Since(start))
})
}
For those who are interested in diving deeper into logging middleware, I recommend checking out Matt Silverlock’s fantastic guide on writing logging middleware in Go. He breaks down how to structure reusable middleware for various use cases like authentication, tracing, and of course, logging!
Step 2: Error Handling Middleware 🚨
Let’s talk about errors. Errors happen, right? But rather than letting them cause a crash or send a vague error message, let’s handle them gracefully.
func errorHandlingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
// Log the error and send a user-friendly message
log.Printf("Error occurred: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
Step 3: Integrating Middleware in Your API 🔗
Now that we’ve built our logging and error-handling middleware, let’s hook them up to our API. We’ll apply them globally so every request gets logged and errors are caught.
func main() {
db = connectDB()
defer db.Close()
r := mux.NewRouter()
// Apply middleware globally
r.Use(loggingMiddleware)
r.Use(errorHandlingMiddleware)
r.HandleFunc("/login", login).Methods("POST")
r.Handle("/books", authenticate(http.HandlerFunc(getBooks))).Methods("GET")
r.Handle("/books", authenticate(http.HandlerFunc(createBook))).Methods("POST")
fmt.Println("Server started on port :8000")
log.Fatal(http.ListenAndServe(":8000", r))
}
Step 4: Testing It Out 🎯
To make sure everything’s working, start up your API:
go run main.go
Now, try hitting any of your endpoints (like /books
) and check your terminal. You should see logs like:
Started GET /books
Completed in 1.2ms
And if there’s an error, you’ll see:
Error occurred: some error details
But your user will only see a clean "500 Internal Server Error" message. 🎉
Why Is This Important?
Logging helps you track down bugs and monitor the behavior of your API. If something goes wrong, you’ll know exactly which endpoint was hit and how long the request took.
Error Handling prevents your API from crashing when something unexpected happens. Instead, it recovers gracefully and sends a clean error message to the client.
What’s Next?
Next time, we’ll take things to the next level and dockerize our Go API! This will make your app portable and ready for deployment on any machine or cloud service. Get ready for some container magic! 🐳
Posted on October 5, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.