feat: logger, middleware
This commit is contained in:
@@ -15,14 +15,17 @@ func (app *application) failedValidationResponse(w http.ResponseWriter, r *http.
|
||||
// book we'll upgrade this to use structured logging, and record additional information
|
||||
// about the request including the HTTP method and URL.
|
||||
func (app *application) logError(r *http.Request, err error) {
|
||||
app.logger.Println(err)
|
||||
app.logger.PrintError(err, map[string]string{
|
||||
"request_method": r.Method,
|
||||
"request_url": r.URL.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// The errorResponse() method is a generic helper for sending JSON-formatted error
|
||||
// messages to the client with a given status code. Note that we're using an interface{}
|
||||
// type for the message parameter, rather than just a string type, as this gives us
|
||||
// more flexibility over the values that we can include in the response.
|
||||
func (app *application) errorResponse(w http.ResponseWriter, r *http.Request, status int, message interface{}) {
|
||||
func (app *application) errorResponse(w http.ResponseWriter, r *http.Request, status int, message any) {
|
||||
env := envelope{"error": message}
|
||||
|
||||
// Write the response using the writeJSON() helper. If this happens to return an
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/joho/godotenv"
|
||||
_ "github.com/lib/pq"
|
||||
"greenlight.debuggingjon.dev/internal/data"
|
||||
"greenlight.debuggingjon.dev/internal/jsonlog"
|
||||
)
|
||||
|
||||
// Declare a string containing the application version number. Later in the book we'll
|
||||
@@ -41,17 +42,20 @@ type config struct {
|
||||
// logger, but it will grow to include a lot more as our build progresses.
|
||||
type application struct {
|
||||
config config
|
||||
logger *log.Logger
|
||||
logger *jsonlog.Logger
|
||||
models data.Models
|
||||
}
|
||||
|
||||
func main() {
|
||||
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
var cfg config
|
||||
|
||||
// Initialize a new jsonlog.Logger which writes any messages *at or above* the INFO
|
||||
// severity level to the standard out stream.
|
||||
logger := jsonlog.New(os.Stdout, jsonlog.LevelInfo)
|
||||
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
logger.PrintFatal(err, nil)
|
||||
}
|
||||
dsn := os.Getenv("DATABASE_DSN")
|
||||
|
||||
@@ -73,7 +77,7 @@ func main() {
|
||||
// application immediately.
|
||||
db, err := openDB(cfg)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
logger.PrintFatal(err, nil)
|
||||
}
|
||||
|
||||
// Defer a call to db.Close() so that the connection pool is closed before the
|
||||
@@ -85,7 +89,7 @@ func main() {
|
||||
}()
|
||||
// Also log a message to say that the connection pool has been successfully
|
||||
// established.
|
||||
logger.Printf("database connection pool established")
|
||||
logger.PrintInfo("database connection pool established", nil)
|
||||
|
||||
app := &application{
|
||||
config: cfg,
|
||||
@@ -98,13 +102,17 @@ func main() {
|
||||
Addr: fmt.Sprintf(":%d", cfg.port),
|
||||
Handler: app.routes(),
|
||||
IdleTimeout: time.Minute,
|
||||
ErrorLog: log.New(logger, "", 0),
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
logger.Printf("starting %s server on %s", cfg.env, srv.Addr)
|
||||
logger.PrintInfo("starting server", map[string]string{
|
||||
"addr": srv.Addr,
|
||||
"env": cfg.env,
|
||||
})
|
||||
err = srv.ListenAndServe()
|
||||
logger.Fatal(err)
|
||||
logger.PrintFatal(err, nil)
|
||||
}
|
||||
|
||||
func openDB(cfg config) (*sql.DB, error) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (app *application) recoverPanic(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Create a deferred function (which will always be run in the event of a panic
|
||||
// as Go unwinds the stack).
|
||||
defer func() {
|
||||
// Use the builtin recover function to check if there has been a panic or
|
||||
// not.
|
||||
if err := recover(); err != nil {
|
||||
// If there was a panic, set a "Connection: close" header on the
|
||||
// response. This acts as a trigger to make Go's HTTP server
|
||||
// automatically close the current connection after a response has been
|
||||
// sent.
|
||||
w.Header().Set("Connection", "close")
|
||||
// The value returned by recover() has the type interface{}, so we use
|
||||
// fmt.Errorf() to normalize it into an error and call our
|
||||
// serverErrorResponse() helper. In turn, this will log the error using
|
||||
// our custom Logger type at the ERROR level and send the client a 500
|
||||
// Internal Server Error response.
|
||||
app.serverErrorResponse(w, r, fmt.Errorf("%s", err))
|
||||
}
|
||||
}()
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func (app *application) routes() *httprouter.Router {
|
||||
func (app *application) routes() http.Handler {
|
||||
router := httprouter.New()
|
||||
|
||||
// Convert the notFoundResponse() helper to a http.Handler using the
|
||||
@@ -24,5 +24,5 @@ func (app *application) routes() *httprouter.Router {
|
||||
router.HandlerFunc(http.MethodPatch, "/v1/movies/:id", app.updateMovieHandler)
|
||||
router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.deleteMovieHandler)
|
||||
|
||||
return router
|
||||
return app.recoverPanic(router)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user