DEV Community

Cover image for Adding Logging and Error Handling Middleware to Your Go API
Neel Patel
Neel Patel

Posted on

Adding Logging and Error Handling Middleware to Your Go API

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))
    })
}


Enter fullscreen mode Exit fullscreen mode

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)
    })
}


Enter fullscreen mode Exit fullscreen mode

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))
}


Enter fullscreen mode Exit fullscreen mode

Step 4: Testing It Out 🎯

To make sure everything’s working, start up your API:



go run main.go


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

And if there’s an error, you’ll see:



Error occurred: some error details


Enter fullscreen mode Exit fullscreen mode

But your user will only see a clean "500 Internal Server Error" message. 🎉


Why Is This Important?

  1. 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.

  2. 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! 🐳

Top comments (0)