In this article, we are going to build a Logging middleware for a Go server built with the package net/http
from the standard library. And using the new slog library added to Go 1.21 to create the logging feature.
This article is for people who are starting to learn the Go programming language and want to log the requests made to their servers.
main.go
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
logger.Info("Request Info",
slog.String("method", r.Method),
slog.String("path", r.RequestURI),
slog.String("host", r.Host),
)
next.ServeHTTP(w, r)
})
}
In the main.go file, we create a LoggingMiddleware()
function, it has an http.Handler
as an argument and returns an http.Handler
.
Inside the function, we create an instance of the Slog to create the TextHandler
.
Then, we create a logger to show the information about the request made to the server, like the HTTP method, the path and the host.
package main
import (
"log/slog"
"net/http"
"os"
)
func main() {
mux := http.NewServeMux()
port := ":8000"
finalHandler := http.HandlerFunc(homeHandler)
mux.Handle("/", LoggingMiddleware(finalHandler))
slog.Info("Listening on ", "port", port)
err := http.ListenAndServe(port, mux)
if err != nil {
slog.Warn("Problem starting the server", "error", err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello this is a home page"))
}
In the main()
function, we create an instance of the ServeMux()
, we define in the port
variable the port where the server is going to listen.
Then, we create the finalHandler
variable to wrap the homeHandler()
handler into the HandlerFunc()
, which is an adapter to allows the use of ordinary functions as HTTP handlers.
For more information about the HandlerFunc type, visit the documentation.
Next, we call the mux.handle()
function, to register the handler for the pattern "/", but instead of just registering the finalHandler, we wrap it into the LoggingMiddleware
. Every request to the path "/" will call the LoggingMiddleware
before the finalHandler()
.
Finally, we create a logger to show info about the port where the server will listen and pass the port
and mux
variable to http.ListenAndServe()
function to start the server.
We start the server and navigate to localhost:8000/
. We should receive the following output in the terminal.
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Helloooo"))
}
func main() {
mux := http.NewServeMux()
port := ":8000"
logger := slog.Default()
finalHandler := http.HandlerFunc(homeHandler)
mux.Handle("/", LoggingMiddleware(finalHandler))
mux.Handle("/hello", LoggingMiddleware(http.HandlerFunc(helloHandler)))
logger.Info("Listening on ", "port", port)
err := http.ListenAndServe(port, mux)
if err != nil {
logger.Error("Problem starting the server", "error", err)
}
}
In the code snippet above we just add another handler, but instead of creating a new variable to apply the http.HandlerFunc
type, we applied directly in the mux.handle()
function.
Conclusion
I wrote this article just for education purposes if anyone wants to create their logging middleware using the new Slog package.
Many web frameworks in Go have a logging middleware like Chi-router or Gorilla. Also, Gin has a logging feature integrated.
Thank you for taking the time to read this article.
If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through Twitter, or LinkedIn.
The source code is here.
Resources
Go REST Guide. The Standard Library
Making and Using HTTP Middleware
How To Make an HTTP Server in Go