Reputation: 748
I want to use the handlers specified here for logging everything.
This is what I have:
r := mux.NewRouter()
s := r.PathPrefix("/api/v1").Subrouter()
s.HandleFunc("/abc", handler.GetAbc).Methods("GET")
s.HandleFunc("/xyz", handler.GetXyz).Methods("GET")
I want to use the logging middleware but I don't want to repeat it in every single line, as they show in github:
r.Handle("/admin", handlers.LoggingHandler(os.Stdout, http.HandlerFunc(ShowAdminDashboard)))
r.HandleFunc("/", ShowIndex)
Is there a way to just pass the general logging middleware to r, and everything that passes the r router will pass by the middleware first?
Upvotes: 4
Views: 9537
Reputation: 2894
This is the approach I took and this worked best for me.
type Route struct {
Name string
Method string
Pattern string
Secure bool
HandlerFunc http.HandlerFunc
}
type Routes []Route
var routes = Routes{
Route{
Name: "Docs",
Method: "GET",
Pattern: "/v2/docs",
HandlerFunc: Docs,
},
Route{
Name: "GetUserByName",
Method: "GET",
Pattern: "/v2/user/{username}",
HandlerFunc: user.GetUserByName,
Secure: true,
},
}
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.NotFoundHandler = http.HandlerFunc(notFound)
router.MethodNotAllowedHandler = http.HandlerFunc(notAllowed)
for _, route := range routes {
var handler http.Handler
if route.Secure {
handler = AuthMiddleware(route.HandlerFunc)
} else {
handler = route.HandlerFunc
}
handler = Logger(os.Stderr, handler)
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(handler)
}
return router
}
func ApplicationRecovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintln(os.Stderr, "Recovered from application error occurred")
_, _ = fmt.Fprintln(os.Stderr, err)
w.WriteHeader(http.StatusInternalServerError)
}))
}
}()
next.ServeHTTP(w, r)
})
}
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
next.ServeHTTP(w, r)
})
}
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//TODO: Add authentication
log.Println("Authentication required")
next.ServeHTTP(w, r)
})
}
func Logger(inner http.Handler, name string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf(
"%s %s %s %s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
inner.ServeHTTP(w, r)
})
}
func notFound(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}
func notAllowed(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusMethodNotAllowed)
}
func main() {
srv := http.Server{
Addr: "0.0.0.0:8080",
Handler: ApplicationRecovery(Middleware(NewRouter())),
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
This way I'm covering my bases:
2020/06/23 22:28:48 Server started
2020/06/23 22:28:51 Authentication required
2020/06/23 22:28:51 Begin x-api-key validation
2020/06/23 22:28:51 x-api-key matched user: 1
2020/06/23 22:28:51 User 1 successfully accessed secure resourecs
::1 - - [23/Jun/2020:22:28:51 +0100] "DELETE /v2/user/john?permanent=true HTTP/1.1" 403 85
Upvotes: 5
Reputation: 748
I wrapped the LoggingHandler with a middleware function
func loggingMiddleware(next http.Handler) http.Handler {
return handlers.LoggingHandler(os.Stdout, next)
}
r.Use(loggingMiddleware)
Upvotes: 5
Reputation: 51657
Use a middleware:
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do stuff here
log.Println(r.RequestURI)
// Call the next handler, which can be another middleware in the chain, or the final handler.
next.ServeHTTP(w, r)
})
}
r.Use(loggingMiddleware)
Here's the doc: https://github.com/gorilla/mux#middleware
Upvotes: 10